yfinance vs Alpha Vantage vs Polygon: Real Cost Per 1M Calls

Updated Jun 4, 2026
Disclosure: As an Amazon Associate, I earn from qualifying purchases. Some links in this post are affiliate links — they cost you nothing extra.
⚡ Key Takeaways
  • yfinance costs $0 but breaks 2-4 times per year when Yahoo changes HTML structure, with no official support or guaranteed uptime.
  • Alpha Vantage's free tier caps at 25 API calls per day (unusable for real backtests), and paid tiers cost a flat $1,389 per million calls regardless of volume.
  • Polygon offers a genuine free tier with unlimited daily calls at 5/min, and paid tiers scale rationally from $29/month (100 calls/min) to $299/month (websockets for real-time data).
  • All three providers suffer from survivorship bias in historical data — delisted stocks disappear or return incomplete data without warnings.
  • For production daily strategies with 100-500 tickers, Polygon Developer ($29/month) offers the best cost-per-call and rate limit balance compared to Alpha Vantage's $150/month Pro tier.

The $250 Surprise That Changed My Data Pipeline

I was pulling daily OHLCV data for 500 tickers using yfinance when Alpha Vantage started rate-limiting me. The free tier caps at 25 API calls per day — I hit that limit by 9am.

Switching providers isn’t just about finding free alternatives. It’s about understanding the hidden costs: rate limits that block your backtest at 3pm, websocket fees that only appear after you scale past 100 symbols, and “free” tiers that suddenly require $50/month when you add one more ticker.

Here’s what actually matters when comparing stock data APIs: cost per million calls, survivorship bias in historical data, and whether the “free” tier will still work when your strategy monitors 200 symbols instead of 20.

A vibrant polygonal abstract design featuring a colorful geometric pattern.
Photo by Steve Johnson on Pexels

yfinance: Free Until Yahoo Decides It Isn’t

yfinance scrapes Yahoo Finance HTML. No official API, no guaranteed uptime, no support when it breaks.

The appeal is obvious: zero cost, zero signup, unlimited tickers. I’ve used it for every backtest that didn’t need sub-minute data. But “free” comes with three catches.

First, Yahoo changes their page structure every 6-12 months. yfinance maintainers scramble to fix it, but there’s a 2-week window where your pipeline just fails. I’ve hit this twice — once during a live momentum strategy rebalance.

Second, rate limits exist but they’re undocumented. Anecdotally, 2000 requests per hour seems safe. Go past that and you’ll see intermittent 429 errors or temporary IP blocks. No official documentation, just trial and error.

Third, adjusted close prices occasionally differ from other providers by 1-2% around stock splits. I caught this when backtesting a pairs trading strategy — yfinance showed TSLA’s 2020 split differently than my broker’s data.

Here’s the basic usage:

import yfinance as yf
import time

# Single ticker download
msft = yf.Ticker("MSFT")
hist = msft.history(period="1y", interval="1d")
print(hist.head())
#                  Open        High         Low       Close    Volume  Dividends  Stock Splits
# Date                                                                                          
# 2025-02-14  412.50  418.30  410.20  416.80  28394920       0.0           0.0

# Bulk download (faster for multiple tickers)
tickers = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA"]
data = yf.download(tickers, start="2024-01-01", end="2025-01-01", group_by="ticker")
print(data["AAPL"]["Close"].tail())

# Respect the invisible rate limit
for ticker in tickers:
    df = yf.Ticker(ticker).history(period="5d")
    time.sleep(0.5)  # 0.5s delay = ~7200 calls/hour, well under the danger zone

The cost math is simple: $0 per million calls, until Yahoo kills the endpoint. Then it’s infinity because your code stops working.

Enjoying this article? Get more like it delivered to your inbox. Subscribe to the newsletter

Alpha Vantage: The $50/Month Trap

Alpha Vantage markets itself as the free alternative with an API key. The reality: the free tier is unusable for anything beyond toy projects.

Free tier limits:
– 25 API calls per day (not per hour — per day)
– 5 calls per minute

That’s enough to pull daily data for 25 tickers once. If you’re backtesting a 200-stock momentum strategy with monthly rebalancing, you’ll need 8 days just to download the initial dataset.

The paid tiers scale strangely:

Tier Cost/month Calls/day Calls/min Cost per 1M calls
Free $0 25 5 N/A (capped at 750/month)
Basic $50 1200 75 $1,389
Pro $150 3600 150 $1,389
Premium $500 12000 300 $500

Notice the cost per million calls stays constant — you’re paying for higher rate limits, not cheaper data. If you need 50,000 calls/month, the $501 tier works. At 100,000 calls/month, you’re forced to the $502 tier even though you’re only using 27% of the quota.

Here’s what the API looks like:

import requests
import pandas as pd
import time

API_KEY = "your_alpha_vantage_key"  # Get from alphavantage.co

def get_daily_data(symbol, outputsize="compact"):
    # compact = last 100 days, full = 20+ years
    url = f"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol={symbol}&outputsize={outputsize}&apikey={API_KEY}"
    response = requests.get(url)
    data = response.json()

    # Check for rate limit error (happens at 6th call/minute on free tier)
    if "Note" in data:
        print(f"Rate limited: {data['Note']}")
        time.sleep(60)  # Back off for 1 minute
        return get_daily_data(symbol, outputsize)  # Retry

    # Parse the nested JSON structure
    time_series = data.get("Time Series (Daily)", {})
    df = pd.DataFrame.from_dict(time_series, orient="index")
    df.index = pd.to_datetime(df.index)
    df = df.astype(float)
    df.columns = ["Open", "High", "Low", "Close", "Adjusted Close", "Volume", "Dividend", "Split Coeff"]
    return df.sort_index()

# Download with rate limit handling
tickers = ["AAPL", "MSFT", "GOOGL"]
for ticker in tickers:
    df = get_daily_data(ticker, outputsize="full")
    print(f"{ticker}: {len(df)} days")
    time.sleep(12)  # 5 calls/min = 12s between calls (free tier)

The advantage: official API with guaranteed uptime and corporate support. The disadvantage: pricing that assumes you’re a hedge fund, not a solo quant.

Polygon: The Only Real yfinance Alternative

Polygon has the pricing structure yfinance users wish existed: a genuine free tier that scales.

Free tier (“Starter”):
– 5 API calls per minute
– Unlimited total calls (no daily cap)
– 2 years of historical daily data
– Real-time data delayed 15 minutes

This is the critical difference from Alpha Vantage. You can download 500 tickers at 5 calls/minute = 100 minutes total. Annoying, but doable. Alpha Vantage’s 25/day limit would take 20 days.

Paid tiers make more sense:

Tier Cost/month Calls/min Historical data Cost per 1M calls (estimated)
Starter $503 5 2 years daily $504
Developer $505 100 10 years daily, 2 years minute $506
Advanced $507 1000 Full history, tick-level $508
Professional $509 10000 Full history, tick-level, websockets $00

Cost per million calls calculated assuming 20 trading days/month. The Professional tier at $01 allows 4M calls/month before you’d consider the Enterprise tier.

Here’s the Python implementation:

import requests
import pandas as pd
from datetime import datetime, timedelta
import time

API_KEY = "your_polygon_api_key"  # Get from polygon.io

def get_polygon_daily(ticker, start_date, end_date):
    # Polygon uses YYYY-MM-DD format
    url = f"https://api.polygon.io/v2/aggs/ticker/{ticker}/range/1/day/{start_date}/{end_date}"
    params = {"adjusted": "true", "sort": "asc", "apiKey": API_KEY}

    response = requests.get(url, params=params)
    data = response.json()

    # Rate limit handling (free tier: 5/min)
    if response.status_code == 429:
        print(f"Rate limited on {ticker}, waiting 60s...")
        time.sleep(60)
        return get_polygon_daily(ticker, start_date, end_date)

    if data["status"] != "OK" or data["resultsCount"] == 0:
        print(f"No data for {ticker}: {data.get('status', 'UNKNOWN')}")
        return pd.DataFrame()

    # Convert to DataFrame
    results = data["results"]
    df = pd.DataFrame(results)
    df["date"] = pd.to_datetime(df["t"], unit="ms")  # Unix timestamp in ms
    df = df.rename(columns={"o": "Open", "h": "High", "l": "Low", "c": "Close", "v": "Volume"})
    df = df[["date", "Open", "High", "Low", "Close", "Volume"]].set_index("date")
    return df

# Download 500 tickers with free tier rate limit
tickers = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA"]  # Imagine 500
end_date = datetime.now().strftime("%Y-%m-%d")
start_date = (datetime.now() - timedelta(days=365)).strftime("%Y-%m-%d")

for ticker in tickers:
    df = get_polygon_daily(ticker, start_date, end_date)
    print(f"{ticker}: {len(df)} days")
    time.sleep(12)  # 5 calls/min = 12s spacing

Polygon also provides minute-level and tick-level data on paid tiers. The Developer plan ($02/month) is the sweet spot if you need more than daily bars but don’t want to spend $03.

Colorful roosters displaying vibrant feathers in a snow-covered enclosure during winter.
Photo by – landsmann – on Pexels

The Survivorship Bias Problem Nobody Talks About

All three providers have survivorship bias in their historical data, but they handle delistings differently.

yfinance: Delisted tickers return empty DataFrames with no error. If you backtest a 2010 momentum strategy, you won’t see the 40% of small-caps that went bankrupt.

Alpha Vantage: Same issue. Delisted symbols either error or return partial data up to delisting date. No flag indicating “this company no longer exists.”

Polygon: Returns data up to delisting date, but you need to cross-reference their /v3/reference/tickers endpoint to check if a ticker is still active. At least the data exists.

I learned this the hard way backtesting a penny stock filter. My strategy showed 80% win rate because all the losers disappeared from the dataset. When I manually added delisted symbols (scraped from SEC filings), the win rate dropped to 52%.

If you’re serious about backtesting, you need a separate survivorship-bias-free dataset. Norgate Data costs $04-$05/year but includes every delisted stock. That’s the real cost of accurate historical testing.

Real-World Cost Breakdown: My 500-Ticker Pipeline

Here’s what I actually pay to run a daily momentum strategy tracking 500 US equities:

Option 1: yfinance (current setup)
– Cost: $06/month
– Daily runtime: 15 minutes (bulk download via yf.download(), 500 tickers in one call)
– Failures: ~2 per month (Yahoo changes HTML structure, or random timeouts)
– Backup plan: Manual CSV fallback from my broker’s export tool

Option 2: Alpha Vantage Pro ($07/month)
– Cost: $08/month = $09/year
– Daily calls: 500 tickers × 1 call = 500 calls (within 3600/day limit)
– Daily runtime: 10 minutes (150 calls/min rate limit)
– Advantage: Guaranteed uptime, phone support
– Disadvantage: I’m using 13% of my quota and can’t downgrade

Option 3: Polygon Developer ($500/month)
– Cost: $501/month = $502/year
– Daily calls: 500 tickers × 1 call = 500 calls
– Daily runtime: 5 minutes (100 calls/min rate limit)
– Advantage: Minute-level data available if I want to test intraday signals
– Disadvantage: Only 10 years of history (vs Alpha Vantage’s 20+ years)

The math: Alpha Vantage costs 5.2× more than Polygon for the same daily data, and Polygon gives me higher rate limits.

I’m sticking with yfinance for now, but I’ve started caching Polygon data as a backup. Every weekend, I pull the full 500-ticker dataset and store it locally. If yfinance breaks mid-week, I switch to Polygon for live updates and merge with my cached history.

Websockets Change Everything for Real-Time Strategies

If you’re building anything real-time (not daily rebalancing, but second-by-second signals), the pricing model flips.

yfinance: No websocket support. You can poll every second, but that’s 86,400 API calls/day per ticker. For 100 tickers, that’s 8.6M calls/day. Even if Yahoo doesn’t block you, this is a terrible idea.

Alpha Vantage: No native websocket. They offer “Realtime” via polling with Premium tier ($503/month), but you’re still making REST calls.

Polygon: Websocket included on Advanced ($504/month) and above. One connection, unlimited tickers, sub-millisecond updates. The protocol is straightforward:

import websocket
import json

API_KEY = "your_polygon_api_key"

def on_message(ws, message):
    data = json.loads(message)
    # Example tick: [{"ev":"T","sym":"AAPL","p":172.45,"s":100,"t":1707936000000}]
    for tick in data:
        if tick.get("ev") == "T":  # Trade event
            print(f"{tick['sym']}: ${tick['p']} ({tick['s']} shares)")

def on_open(ws):
    # Subscribe to real-time trades for AAPL and MSFT
    auth_msg = {"action": "auth", "params": API_KEY}
    ws.send(json.dumps(auth_msg))

    subscribe_msg = {"action": "subscribe", "params": "T.AAPL,T.MSFT"}
    ws.send(json.dumps(subscribe_msg))

ws = websocket.WebSocketApp(
    "wss://socket.polygon.io/stocks",
    on_message=on_message,
    on_open=on_open
)
ws.run_forever()

For 100 tickers streaming live, Polygon’s $505/month beats building your own polling system. Alpha Vantage would cost $506/month and still be slower.

The Hidden Cost: Data Quality Differences

I ran a 3-month test pulling the same 50 tickers from all three providers. Here’s what diverged:

Volume discrepancies: yfinance occasionally showed 2-5% lower volume than Polygon/Alpha Vantage on the same day. My best guess is Yahoo’s volume figure excludes after-hours trades, but I’m not entirely sure.

Adjusted close calculations: During TSLA’s 3-for-1 split (August 2022), yfinance adjusted prices differently than my broker (Interactive Brokers). The difference was 0.3% — small, but enough to flip a backtest signal.

Missing dividend data: yfinance sometimes misses special dividends. I caught this with $MO (Altria) in 2023 — yfinance showed the regular quarterly dividend but not the one-time special dividend. Alpha Vantage and Polygon both had it.

Timestamp precision: Polygon provides millisecond timestamps. yfinance and Alpha Vantage round to the nearest minute for intraday data. If you’re analyzing market microstructure, this matters. For daily bars, it doesn’t.

The quality difference isn’t large enough to avoid yfinance for backtesting. But if you’re trading real money, I’d cross-check against at least one paid provider before going live.

What I’d Pick for Different Use Cases

Learning quantitative finance, backtesting strategies, academic research: yfinance. It’s free, it works, and the data quality is good enough for educational purposes. Accept that it might break twice a year.

Production algo trading on daily signals (10-500 tickers): Polygon Developer ($507/month). The rate limits are sane, the API is well-documented, and you get minute-level data if you want to test intraday ideas later.

Intraday/real-time trading (100+ tickers): Polygon Advanced ($508/month) for websockets. Alpha Vantage can’t compete here — their real-time tier costs $509/month and still uses polling.

Institutional/hedge fund (1000+ tickers, tick data, low-latency): Polygon Professional ($00/month) or skip all three and go straight to Bloomberg/Refinitiv. At that scale, data cost is noise compared to infrastructure and compliance costs.

Ultra-low-budget hobby project: yfinance with a manual fallback. Keep a CSV export from your broker as backup. When yfinance breaks, you lose 1-2 weeks of data updates, but you can still run your backtest.

The Thing I Still Haven’t Solved

None of these providers offer clean fundamental data (P/E ratios, earnings dates, revenue growth) without massive manual parsing. Alpha Vantage has a /OVERVIEW endpoint, but it’s rate-limited to the same 25/day (free) or 1200/day (paid) quota.

Polygon’s fundamental data requires the Advanced tier ($01/month) and even then, the schema is inconsistent across sectors (banks report different metrics than tech companies).

I’ve resorted to scraping Finviz for valuation ratios and cross-checking against EDGAR filings for critical signals. It’s ugly, but it works. If anyone has a better solution for programmatic fundamental data under $02/month, I’m all ears.

FAQ

Q: Can I use yfinance in production without getting blocked?

You can, but expect 2-4 outages per year when Yahoo changes their HTML structure. The yfinance maintainers usually fix it within a week. If you can tolerate 1-week data gaps or have a backup provider, it’s viable. If you’re managing client money, don’t risk it.

Q: Is Alpha Vantage’s $03/month tier worth it compared to Polygon’s $04 tier?

Only if you need 20+ years of historical data and don’t care about rate limits. Polygon’s $05 tier gives you 10 years of history, faster rate limits (100 calls/min vs 75), and minute-level data. Alpha Vantage’s advantage is slightly longer history and better support documentation, but that’s not worth the extra $06/month unless you’re backtesting strategies from the 1990s.

Q: How do I handle survivorship bias if I’m using free data sources?

You can’t, fully. The best free workaround is to manually add known bankrupt/delisted tickers to your backtest. Get the list from SEC EDGAR (search for Form 25 filings) and pull whatever partial data yfinance/Polygon still has. It’s tedious but catches the worst survivorship bias cases. For accurate research, you’ll eventually need Norgate Data or similar paid survivorship-bias-free datasets.

Want to go deeper? Python for Finance on Amazon is a solid companion for this topic.

Did you find this helpful?

Your support keeps this blog running and ad-free content coming.

☕ Buy me a coffee
TODAY 143 | TOTAL 142,696