DEV Community

Ava Torres
Ava Torres

Posted on

How to Get Historical Stock Data from Yahoo Finance (Without Paying for an API Key)

Yahoo Finance is one of the last free sources for historical stock price data. Most alternatives (Alpha Vantage, Polygon, IEX) either rate-limit aggressively on free tiers or charge hundreds per month for real coverage.

The catch: Yahoo doesn't officially publish an API anymore. But the v8 chart endpoint that powers their website still works, returns clean JSON, and covers decades of historical data. I've been pulling data from it for a while and want to document what actually works in 2026.

What you can get

The v8 chart API returns:

  • Historical OHLCV data (open, high, low, close, volume) going back to IPO for most tickers
  • Adjusted close prices accounting for splits and dividends
  • Dividend and stock split history
  • Multiple intervals: 1m, 5m, 15m, 1h, 1d, 1wk, 1mo
  • Pre/post market data for intraday intervals

This covers US stocks, ETFs, mutual funds, indices, crypto, and many international exchanges.

The v8 chart endpoint

GET https://query1.finance.yahoo.com/v8/finance/chart/AAPL?range=1y&interval=1d
Enter fullscreen mode Exit fullscreen mode

Key parameters:

Parameter Description Example
range Time range 1d, 5d, 1mo, 3mo, 6mo, 1y, 5y, max
interval Data interval 1m, 5m, 15m, 1h, 1d, 1wk, 1mo
period1 Start timestamp (unix) 1609459200
period2 End timestamp (unix) 1704067200
events Include dividends/splits div,splits

You can use either range or period1/period2, not both. The events parameter adds dividend and split arrays to the response.

What the response looks like

The JSON structure is nested but predictable:

{
  "chart": {
    "result": [{
      "meta": {
        "symbol": "AAPL",
        "currency": "USD",
        "exchangeName": "NMS",
        "regularMarketPrice": 178.72
      },
      "timestamp": [1672531200, 1672617600, ...],
      "indicators": {
        "quote": [{
          "open": [130.28, 128.41, ...],
          "high": [130.90, 130.02, ...],
          "low": [128.12, 127.77, ...],
          "close": [129.93, 129.12, ...],
          "volume": [42628800, 38986400, ...]
        }],
        "adjclose": [{
          "adjclose": [129.62, 128.81, ...]
        }]
      }
    }]
  }
}
Enter fullscreen mode Exit fullscreen mode

Timestamps are Unix epoch seconds. Prices are floats. Volume is integer. Null values appear for market holidays or missing data points.

Important gotchas

1. You need a browser User-Agent header. The endpoint rejects requests with default library User-Agents (like python-requests/2.28). Set something like:

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Enter fullscreen mode Exit fullscreen mode

2. Intraday data has retention limits. 1-minute data only goes back about 7 days. 5-minute goes back ~60 days. Daily and weekly data goes back to IPO.

3. Rate limiting exists but isn't documented. From testing, staying under ~2 requests/second works fine. Burst too fast and you'll get 429s or empty responses.

4. The v7 quote endpoint requires authentication now. The older /v7/finance/quote endpoint that returned real-time quotes now requires a crumb/cookie auth flow. The v8 chart endpoint doesn't.

5. Adjusted close matters for backtesting. If you're comparing prices across time periods, always use adjclose -- it accounts for splits and dividends that would otherwise make the raw close prices misleading.

Getting multiple tickers

The endpoint handles one ticker per request. For a portfolio of tickers, you'll need to loop:

import requests
import time

tickers = ["AAPL", "MSFT", "GOOGL", "AMZN", "NVDA"]
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"}

for ticker in tickers:
    url = f"https://query1.finance.yahoo.com/v8/finance/chart/{ticker}"
    params = {"range": "1y", "interval": "1d", "events": "div,splits"}
    resp = requests.get(url, params=params, headers=headers)
    data = resp.json()["chart"]["result"][0]

    timestamps = data["timestamp"]
    quotes = data["indicators"]["quote"][0]
    print(f"{ticker}: {len(timestamps)} data points")

    time.sleep(0.5)  # stay under rate limits
Enter fullscreen mode Exit fullscreen mode

Dividend and split data

When you add events=div,splits to the request, you get extra fields:

{
  "events": {
    "dividends": {
      "1675900800": {
        "amount": 0.23,
        "date": 1675900800
      }
    },
    "splits": {
      "1598832000": {
        "date": 1598832000,
        "numerator": 4,
        "denominator": 1,
        "splitRatio": "4:1"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This is particularly useful for income-focused analysis or understanding sudden price changes in historical data.

When you need to scale this up

If you need to pull data for hundreds of tickers on a schedule, the manual approach breaks down fast. You hit rate limits, need to handle retries, store results, and deal with tickers that get delisted or renamed.

I built an Apify actor for Yahoo Finance data that handles all of this -- concurrent fetching, rate limit management, error handling, and outputs clean JSON or CSV. It costs $0.002 per ticker lookup, which works out to about $1 for a 500-stock portfolio update.

Useful if you're running scheduled portfolio scans, building datasets for backtesting, or pulling dividend histories across a large universe of tickers.

Alternatives worth knowing about

  • Alpha Vantage: Free tier gives 25 req/day. Good for casual use, painful for anything at scale.
  • Polygon.io: Solid API, starts at $29/month. Worth it if you need real-time data.
  • FRED (Federal Reserve): Free, great for economic indicators, but no individual stock data.
  • Quandl/Nasdaq Data Link: Was free, now mostly paywalled after Nasdaq acquisition.

Yahoo Finance remains the best option for free historical stock data at any reasonable scale. The lack of official documentation is the only real downside -- but the v8 endpoint has been stable for years.

Top comments (0)