yfinance rate limits

yfinance rate limit: causes, fixes and alternatives

A yfinance rate limit is not a published number. yfinance calls an unofficial Yahoo endpoint with no documented limits and no support, so throttling shows up as a 429 or an empty DataFrame. This guide covers what is actually being limited, how to make fewer calls, and when to move to a keyed API.

Get a free API key See pricing Download free samples

What the yfinance rate limit actually is

There is no published yfinance rate limit because there is no published API. yfinance is a popular open-source project that sends requests to the same unofficial Yahoo endpoints the Yahoo Finance site uses internally. Yahoo does not document those endpoints, does not publish limits for them, and does not offer support to third-party callers. Whatever throttling exists can change at any time, per IP, per header or per endpoint.

So treat the limit as unknown. Numbers circulated online are anecdotes from one machine on one day, not a contract. When your job fails there is no status page to check and no ticket to file, which is the real cost of depending on an unofficial interface.

Why you get empty frames instead of errors

Failures rarely arrive as clean errors. You either get an HTTP 429 "Too Many Requests" response, or yfinance returns an empty DataFrame and your loop keeps running with nothing in it. The empty-frame case is worse because it fails silently: downstream code sees zero rows, computes nothing, and reports success.

Both symptoms share a cause — the endpoint declined to serve you — so the fix is the same. Call less, cache more, and add explicit empty-frame checks so silence never propagates into a backtest or a report.

Cache first, call once

import finzdata as yf
import pandas as pd

frames = {}
for sym in ["AAPL", "MSFT", "SPY"]:
    t = yf.Ticker(sym)
    frames[sym] = t.history(start="2024-01-01", interval="1d")
panel = pd.concat(frames, names=["symbol", "date"])
panel.to_parquet("daily.parquet")  # read this back instead of re-calling

Reduce calls before you retry

Retries with backoff help with a transient 429, but they do not fix a sustained yfinance rate limit — they just spread the same call volume over more time. Reduce the volume first.

  • Request the longest history you need once, then read from local storage instead of re-calling for every backtest.
  • Batch symbols where the library supports it rather than looping one request per ticker per field.
  • Drop fields you never use; every extra field is another reason to re-fetch.
  • Run ingestion nightly, not on demand, so dashboards read a cache rather than Yahoo.

When to move to a keyed API

Move when any of these is true: you need more than about a year of daily bars, you need intraday bars at scale, you need delisted names for survivorship-free backtests, or an empty frame would silently corrupt results. A keyed API publishes its limits, returns structured errors you can handle in code, and gives you someone to ask when something breaks.

Free tiers make this cheap to test. FinzData's free tier covers the last 12 months of every dataset with 1,000 API calls per day and no card, so you can compare outputs against yfinance before changing a single line of your pipeline.

The same calls in FinzData

import finzdata as yf

t = yf.Ticker("AAPL")
df = t.history(start="2000-01-01", interval="1d")

c = yf.Client()
px = c.prices("AAPL", adjust="all")

A quick checklist

Before you blame the limit, check the basics. Log every empty DataFrame with the symbol and timestamp. Store raw responses to parquet or CSV so you never re-fetch what you already pulled. Keep one ingestion job, not five scripts each hitting Yahoo. And when a job starts failing every day, treat that as a signal to move to an interface with published limits, not as a reason to add more retries.

Frequently asked questions

What is yfinance's rate limit?

There is no published one. yfinance scrapes an unofficial Yahoo endpoint, and Yahoo publishes no limits and offers no support for it. Any number you see online is anecdotal.

Why does yfinance return an empty DataFrame?

Usually because the endpoint declined the request — throttling, a blocked header or a changed response shape. Treat empty frames as errors, log them, and stop the run.

How do I fix 'Too Many Requests' in yfinance?

Cache results to disk, request long histories once, batch symbols, and space jobs out. Backoff alone just spreads the same volume over more time.

Does yfinance support an API key?

No. There is no key that raises your allowance, which is why heavy users eventually move to a keyed API with published limits and structured errors.

What replaces yfinance for long histories?

A keyed market-data API. FinzData's Python client mirrors yfinance — Ticker, history and pandas DataFrames — with 16,000+ US stocks since January 2000, including delisted names.