GuidesYAHOO FINANCE

Yahoo Finance API from JavaScript

Prefix https://proxy.cors.dev/ to any Yahoo Finance chart URL: free, no signup, no key, 600 requests per minute, JSON up to 1 MiB. Yahoo sends no Access-Control-Allow-Origin header, so the same fetch() straight from your page fails with a CORS error. The v8 chart endpoint returns the current price, previous close, day range, 52-week range, volume and price history for stocks, indexes, ETFs, currencies and crypto. The quote, quoteSummary and options endpoints need a Yahoo cookie, so they return 401 through cors.dev, which never forwards cookies.

Why fetch() to Yahoo Finance fails

Yahoo has no documented public API. The JSON endpoints on query1.finance.yahoo.com are the ones its own pages call, and they answer without an Access-Control-Allow-Origin header. Your browser sends the request, receives the prices, and then refuses to hand them to your script: Chrome logs blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Setting mode: 'no-cors' does not help, it returns an empty opaque response.

A proxy fixes this because the request to Yahoo then runs on a server, where CORS does not apply, and the response comes back with Access-Control-Allow-Origin set to your origin. With cors.dev the only change is the URL: https://proxy.cors.dev/ followed by the full Yahoo URL. There is nothing to install and no account to create.

Get a stock price in the browser

The chart endpoint with range=1d&interval=1d is the smallest call that carries a full quote: about 1.3 KB, with the price, previous close, day high and low, 52-week high and low, volume, currency and exchange in chart.result[0].meta. Encode the symbol, because index and currency symbols contain ^ and =.

Current price for any Yahoo Finance symbol, from the browser
// Runs in a module script or the DevTools console
const proxy = 'https://proxy.cors.dev/';

async function getQuote(symbol) {
  const url = `https://query1.finance.yahoo.com/v8/finance/chart/${encodeURIComponent(symbol)}?range=1d&interval=1d`;
  const response = await fetch(proxy + url, { credentials: 'omit' });
  const body = await response.json().catch(() => null);

  if (!response.ok) {
    const reason = response.headers.get('X-Cors-Error') ?? body?.chart?.error?.description ?? response.status;
    throw new Error(`${symbol}: ${reason}`);
  }

  const { meta } = body.chart.result[0];
  return {
    symbol: meta.symbol,
    name: meta.longName ?? meta.shortName,
    price: meta.regularMarketPrice,
    change: meta.regularMarketPrice - meta.chartPreviousClose,
    previousClose: meta.chartPreviousClose,
    dayRange: [meta.regularMarketDayLow, meta.regularMarketDayHigh],
    yearRange: [meta.fiftyTwoWeekLow, meta.fiftyTwoWeekHigh],
    volume: meta.regularMarketVolume,
    currency: meta.currency,
    exchange: meta.fullExchangeName,
    time: new Date(meta.regularMarketTime * 1000),
  };
}

const quotes = await Promise.all(['AAPL', '^GSPC', 'EURUSD=X', 'BTC-USD'].map(getQuote));
console.table(
  quotes.map(({ symbol, price, change, currency }) => ({ symbol, price, change: change.toFixed(2), currency })),
);

Errors come in two shapes. Yahoo's own errors keep their JSON body, for example 404 with No data found, symbol may be delisted for an unknown ticker. Proxy-side failures set the X-Cors-Error response header, which cors.dev exposes to your script, for example response_too_large or rate_limited.

Price history for a chart

The same endpoint returns bars for a range and an interval. timestamp holds Unix seconds and indicators.quote[0] holds matching open, high, low, close and volume arrays; daily and longer intervals add indicators.adjclose. A bar can be null when nothing traded, so filter before you plot.

Daily price history for a chart
async function getHistory(symbol, range = '1y', interval = '1d') {
  const url = `https://query1.finance.yahoo.com/v8/finance/chart/${encodeURIComponent(symbol)}?range=${range}&interval=${interval}`;
  const response = await fetch('https://proxy.cors.dev/' + url, { credentials: 'omit' });
  if (!response.ok) {
    throw new Error(`${symbol}: ${response.headers.get('X-Cors-Error') ?? response.status}`);
  }

  const [result] = (await response.json()).chart.result;
  const quote = result.indicators.quote[0];
  const adjusted = result.indicators.adjclose?.[0].adjclose;

  return (result.timestamp ?? [])
    .map((seconds, i) => ({
      time: new Date(seconds * 1000),
      open: quote.open[i],
      high: quote.high[i],
      low: quote.low[i],
      close: quote.close[i],
      adjClose: adjusted?.[i] ?? quote.close[i],
      volume: quote.volume[i],
    }))
    .filter((bar) => bar.close !== null);
}

const bars = await getHistory('AAPL', '1y', '1d');
console.log(`${bars.length} daily bars, last close ${bars.at(-1).close}`);

Ranges are 1d, 5d, 1mo, 3mo, 6mo, 1y, 2y, 5y, 10y, ytd and max. Intervals are 1m, 2m, 5m, 15m, 30m, 60m, 90m, 1h, 1d, 5d, 1wk, 1mo and 3mo. Yahoo serves 1m bars for at most 8 days per request and answers 422 beyond that, and range=max quietly switches to 3-month bars. For long daily history, pass period1 and period2 as Unix seconds with interval=1d, in spans that stay under 1 MiB.

Endpoints that work, and the ones that do not

Paths are on https://query1.finance.yahoo.com. Sizes are what the proxy relayed for the example symbols.

Yahoo Finance endpoints through cors.dev, checked October 2026
PathReturnsSizeFree tier
/v8/finance/chart/AAPL?range=1d&interval=1dPrice, previous close, day and 52-week range, volume1.3 KBWorks
/v8/finance/chart/AAPL?range=1d&interval=5mToday in 5-minute bars8 KBWorks
/v8/finance/chart/AAPL?range=1y&interval=1d251 daily bars with adjusted close28 KBWorks
/v8/finance/chart/AAPL?range=5y&interval=1d&events=div,splitFive years of daily bars with dividends and any splits140 KBWorks
/v8/finance/chart/AAPL?period1=0&period2=9999999999&interval=1dEvery daily bar since 1980Over 1 MiB502 response_too_large
/v7/finance/spark?symbols=AAPL,MSFT,NVDA&range=1d&interval=5mClose series for up to 20 symbols8 KB for 3 symbolsWorks
/v1/finance/search?q=appleTicker search with names and exchanges9 KBWorks
/v1/finance/trending/US20 trending symbols0.5 KBWorks
/v7/finance/quote?symbols=AAPLFull quoten/a401, needs a cookie and crumb
/v10/finance/quoteSummary/AAPL?modules=priceFundamentals and profilen/a401 Invalid Crumb
/v7/finance/options/AAPLOptions chainn/a401 Invalid Crumb

The 401 rows are not something a paid plan fixes. Those endpoints check a crumb tied to a Yahoo session cookie, and cors.dev drops cookies on every plan. The chart meta already carries price, change, ranges and volume; for fundamentals you need a server or a licensed data provider.

Limits to plan for

  • cors.dev free tier: GET and HEAD, 600 requests per minute per client IP, no daily or monthly quota, text responses up to 1 MiB and 10 seconds for Yahoo to answer. The limit counts per client IP, so each visitor has their own.
  • Yahoo itself: these endpoints are unofficial, change without notice and can answer 429 Too Many Requests at busy times. Cache quotes for a few seconds and history for hours, and retry after a pause.
  • Size: five years of daily bars with dividends is about 140 KB, but every daily bar of AAPL since 1980 is over 1 MiB and returns 502 with X-Cors-Error: response_too_large. Pro relays up to 6 MiB for $5 per month.
  • Headers: send a plain fetch(). The free tier forwards only Accept, If-None-Match and If-Modified-Since and answers 400 header_not_allowed for Authorization and custom headers such as X-Api-Key.
  • Paid products: these endpoints come with no documentation, no uptime promise and no notice of changes. If you sell what you build, use a licensed market data API instead.

Good questions.

Is there an official Yahoo Finance API?

No public, documented one. The query1.finance.yahoo.com endpoints are the ones the Yahoo Finance website uses. They need no key and return JSON, but they can change or disappear without notice, so keep every call in one small function you can swap out.

Why does /v7/finance/quote return 401?

The quote, quoteSummary and options endpoints require a crumb that matches a Yahoo session cookie. Your page cannot send Yahoo cookies, and cors.dev never forwards cookies on any plan, so these answer 401 with User is unable to access this feature or Invalid Crumb. Read the same price fields from the v8 chart meta instead.

How do I get prices for several symbols in one request?

Use /v7/finance/spark?symbols=AAPL,MSFT,NVDA&range=1d&interval=5m. It returns a close series and basic meta per symbol, up to 20 symbols per call; more than 20 returns 400 with Number of symbols needs to be less than or equal to 20. For the full meta per symbol, run chart calls in parallel.

Do indexes, currencies and crypto work?

Yes. Use Yahoo symbols such as ^GSPC for the S&P 500, EURUSD=X for euro to dollar and BTC-USD for bitcoin, and pass them through encodeURIComponent so ^ and = survive in the URL path. Exchange suffixes work too, for example ASML.AS for Euronext Amsterdam.

How many requests can my page make?

The free tier allows 600 requests per minute per client IP with no daily or monthly quota, and every visitor counts separately. Yahoo can still answer 429 when it sees too much traffic, so cache results and back off instead of retrying in a loop.

Do I need the proxy in Node.js or Python?

No. Browsers enforce CORS and servers do not, so server code can call query1.finance.yahoo.com directly. The proxy is for code that runs in a browser: static sites, single-page apps and dashboards without a backend.