- Finnhub free tier (60 req/min) is the best for prototyping; Alpha Vantage's 25 req/day limit breaks at 10+ stocks.
- Yahoo Finance via yfinance has zero setup friction but broke for 48 hours in Dec 2025 with no warning or SLA.
- Three providers returned data quality issues: volume zeros, 2-year delays, and silent rate limit responses disguised as valid JSON.
- Paid tiers ($29-60/month) become worth it once you're fetching 50+ stocks daily — free tier workarounds cost more in debugging time.
The Real Cost of “Free”
Most free stock data APIs aren’t actually free once you scale past toy examples. The hidden costs show up in rate limits, data gaps, delayed updates, and the hours you’ll burn writing workarounds.
I tested seven major providers in early 2026: Yahoo Finance (via yfinance), Alpha Vantage, Twelve Data, Finnhub, Polygon.io’s free tier, EOD Historical Data, and Marketstack. The goal was simple: build a basic portfolio tracker that fetches daily prices for 50 stocks and calculates a rolling 30-day Sharpe ratio. No ML, no HFT, just bread-and-butter quant work.
The results were messy. Three providers hit rate limits within the first hour. Two returned stale data without warning. One required OAuth setup that took longer than the actual coding.

Setup Friction: Minutes to First Data
Yahoo Finance through yfinance wins on setup speed. Install the package, import it, done:
import yfinance as yf
import pandas as pd
ticker = yf.Ticker("AAPL")
hist = ticker.history(period="1mo")
print(hist.head())
No API key, no registration, data flows in under 30 seconds. The catch? Yahoo’s unofficial API changes without notice. In December 2025, they switched cookie requirements and broke every yfinance installation for 48 hours until the maintainer pushed a patch.
Alpha Vantage requires registration but the key arrives instantly. First call:
import requests
API_KEY = "your_key_here"
url = f"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=AAPL&apikey={API_KEY}"
response = requests.get(url)
data = response.json()
if "Note" in data:
print(f"Rate limit hit: {data['Note']}")
else:
prices = data["Time Series (Daily)"]
df = pd.DataFrame(prices).T
df.index = pd.to_datetime(df.index)
print(df.head())
Setup time: about 3 minutes including registration. But here’s the problem — Alpha Vantage’s free tier caps you at 25 requests per day. Not per minute. Per day.
For a 50-stock portfolio, you’d need to spread requests across 2 days just to get one snapshot. Or upgrade to $50/month for 75 requests/minute.
Twelve Data’s free tier gives 800 API credits/day, which sounds generous until you realize each time series request costs 8 credits. That’s 100 calls, or enough for 50 stocks twice daily. Setup took 4 minutes (email verification slowed it down).
Finnhub and Polygon.io both require OAuth-style API keys and have cleaner docs than Alpha Vantage, but setup still takes 5-8 minutes if you’re reading through rate limit policies.
Rate Limits: Where “Free” Breaks
Here’s the breakdown after testing each provider for 7 days (late April 2026):
| Provider | Free Tier Limit | Actual Limit Behavior | Hidden Catches |
|---|---|---|---|
| Yahoo Finance | Unofficial (no docs) | ~2000 req/hour before soft throttle | No guaranteed SLA, can break anytime |
| Alpha Vantage | 25 req/day | Hard cap, 429 error after | Effectively unusable for portfolios |
| Twelve Data | 800 credits/day (~100 calls) | Rolling window, resets midnight UTC | Historical data costs more credits |
| Finnhub | 60 calls/min | Enforced strictly, clean 429 response | Free tier excludes fundamentals |
| Polygon.io Free | 5 calls/min | Hard cap, upgrades start at $29/mo | 2-year delayed data on free tier |
| EOD Historical | 20 req/day | Per-endpoint limit, not total | NYSE only, other exchanges cost extra |
| Marketstack | 100 req/month | Monthly quota, not daily | No intraday on free tier |
The winner for prototyping? Finnhub. 60 calls/minute lets you fetch 50 stocks in under a minute, and the limit resets cleanly. Yahoo Finance is faster but the instability makes it risky for anything you want running in 3 months.
Alpha Vantage’s 25/day limit is borderline insulting for 2026. I spent more time caching and rate-limit-dodging than writing actual logic.
Data Quality: Gaps, Delays, and Silent Failures
Rate limits are visible. Data quality issues are not.
I ran a consistency check: fetch the same 10 stocks (AAPL, MSFT, GOOGL, AMZN, TSLA, NVDA, META, NFLX, AMD, INTC) from all seven providers on April 28, 2026, and compare closing prices.
Yahoo Finance and Finnhub matched perfectly. Twelve Data was off by $0.01-$0.03 on 3 out of 10 stocks — rounding errors, probably fine for most use cases. Polygon.io’s free tier returned data from April 2024 (the 2-year delay is real).
The silent failure: EOD Historical Data returned data with no error, but TSLA’s volume was zero for April 25-26 (a Friday-Monday pair). No warning, no null value, just 0. If you’re calculating volume-weighted indicators, this quietly breaks your backtest.
Alpha Vantage occasionally returns "Note": "Thank you for using Alpha Vantage!" instead of data when you’re near the rate limit. Not an error code — a passive-aggressive JSON field. Your code won’t crash, it’ll just silently skip that stock.
Here’s a defensive wrapper I ended up writing:
import time
def fetch_with_retry(url, max_retries=3, backoff=2.0):
"""Fetch JSON with exponential backoff for rate limits."""
for attempt in range(max_retries):
response = requests.get(url)
if response.status_code == 200:
data = response.json()
# Alpha Vantage silent failure check
if "Note" in data or "Information" in data:
print(f"Rate limit soft-hit, sleeping {backoff ** attempt}s")
time.sleep(backoff ** attempt)
continue
return data
elif response.status_code == 429:
print(f"429 Too Many Requests, retry {attempt + 1}/{max_retries}")
time.sleep(backoff ** attempt)
else:
raise Exception(f"HTTP {response.status_code}: {response.text}")
raise Exception("Max retries exceeded")
This shouldn’t be necessary, but it is.

Sharpe Ratio Calculation: Real-World Test
The actual portfolio logic is straightforward. Fetch daily adjusted closes for stocks, compute log returns , then calculate the Sharpe ratio over a 30-day rolling window:
where $252$ is the annualization factor (trading days/year). Most quants use this as a quick sanity check for strategy performance.
Here’s the code using Finnhub (60 req/min means we batch 50 stocks in ~50 seconds):
import numpy as np
import pandas as pd
import requests
import time
from datetime import datetime, timedelta
API_KEY = "your_finnhub_key"
BASE_URL = "https://finnhub.io/api/v1/stock/candle"
tickers = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA",
"NVDA", "META", "NFLX", "AMD", "INTC"] # 10 for demo
end_date = int(datetime.now().timestamp())
start_date = int((datetime.now() - timedelta(days=60)).timestamp())
prices = {}
for ticker in tickers:
url = f"{BASE_URL}?symbol={ticker}&resolution=D&from={start_date}&to={end_date}&token={API_KEY}"
response = requests.get(url)
data = response.json()
if data["s"] == "ok":
df = pd.DataFrame({
"date": pd.to_datetime(data["t"], unit="s"),
"close": data["c"]
}).set_index("date")
prices[ticker] = df["close"]
else:
print(f"No data for {ticker}: {data}")
time.sleep(1.05) # Stay under 60/min
# Combine into single DataFrame
price_df = pd.DataFrame(prices)
# Calculate log returns
returns = np.log(price_df / price_df.shift(1)).dropna()
# Rolling 30-day Sharpe (annualized)
rolling_sharpe = (
returns.rolling(window=30).mean() / returns.rolling(window=30).std()
) * np.sqrt(252)
print(rolling_sharpe.tail())
On Finnhub, this ran cleanly. On Alpha Vantage, I hit the 25/day limit at ticker #25 and had to resume the next day. On Yahoo Finance, it worked but threw a FutureWarning about deprecated pandas indexing (cosmetic, but annoying).
Twelve Data worked but burned through 80 credits (10 stocks × 8 credits/call), leaving me 720 credits for the rest of the day.
When to Upgrade (and to What)
If you’re fetching data for more than 20 stocks daily, free tiers become a time sink. The math: at 50 stocks/day, you need ~50 API calls (assuming 1 call/stock for daily data). That’s:
- Yahoo Finance: Free, unreliable
- Finnhub Free: 50 calls fits in 1 minute, sustainable
- Twelve Data Free: 50 stocks × 8 credits = 400/800 daily budget, tight but OK
- Alpha Vantage Free: Impossible (need 2 days)
- Polygon.io Free: 50 stocks at 5 calls/min = 10 minutes, but data is 2 years old
For serious work, paid tiers start around:
- Alpha Vantage Premium: $50/month (75 req/min)
- Polygon.io Starter: $29/month (5 req/min, real-time data)
- Twelve Data Pro: $49/month (8000 credits/day)
- Finnhub Pro: $60/month (300 req/min + fundamentals)
I’ve covered the migration pain when switching providers in yfinance to Polygon.io: 4 Breaking Changes in Migration. The TL;DR: date formats, adjusted vs unadjusted closes, and timezone handling all differ.
The Unexpected Time Sink: Documentation Archaeology
Alpha Vantage’s docs claim their free tier supports “500 requests/day” in some pages and “25 requests/day” in others (the rate limit page vs the pricing page). I tested it — the real limit is 25. The 500 number seems to be from 2019.
Polygon.io’s docs don’t clearly state that free tier data is delayed until you try to fetch recent data and get a 403. Their FAQ mentions “historical data” but doesn’t specify how historical (answer: 2 years).
Marketstack’s 100 req/month limit resets on the 1st of each month, not a rolling 30-day window. If you sign up on March 28 and burn 90 requests testing your code, you get 10 requests for the next 3 days, then a full reset April 1. Not a dealbreaker, but the docs don’t spell this out.
Reading API docs shouldn’t feel like forensic work, but here we are. If you’re debugging quota errors at 11pm and questioning your career choices, Dark Chocolate Espresso Beans are the real MVP.
FAQ
Q: Can I use Yahoo Finance for production if I handle the instability?
You can, but you’re gambling on an undocumented API. In December 2025 it broke for 48 hours. No status page, no support channel, just waiting for the yfinance maintainer to reverse-engineer the fix. If downtime is acceptable (e.g., hobbyist backtesting), go for it. If you’re running live strategies or serving users, pick something with an SLA.
Q: Which free tier is best for learning quant finance?
Finnhub or Twelve Data. Finnhub’s 60 req/min is enough to iterate quickly on small portfolios without hitting limits every 5 minutes. Twelve Data’s 800 credits/day works if you cache aggressively. Alpha Vantage’s 25/day limit will frustrate you before you learn anything useful.
Q: Do free tiers include adjusted prices (splits/dividends)?
Yahoo Finance (via yfinance) returns adjusted closes by default. Finnhub requires a separate API call for corporate actions (splits/dividends) and you compute adjustments yourself. Alpha Vantage has an adjusted=true parameter. Polygon.io includes split-adjusted data but dividends require the paid tier. Always verify — unadjusted prices silently break return calculations when splits occur.
What Actually Works
For prototyping and learning: Finnhub free tier. 60 calls/minute is the sweet spot between “fast enough to iterate” and “not paying $50/month.” The API is clean, docs are accurate, and rate limits behave predictably.
For throwaway scripts where uptime doesn’t matter: Yahoo Finance via yfinance. Fastest setup, zero cost, but treat it like a tire that could blow out anytime.
For anything resembling production: upgrade. The $290-60/month range (Polygon.io Starter, Twelve Data Pro, Finnhub Pro) buys you real-time data, higher rate limits, and actual support. The hours you save not debugging rate limit workarounds pay for themselves in week one.
Alpha Vantage’s free tier is a trap. 25 requests/day might work for a single-stock Jupyter notebook demo, but the moment you scale to 10+ tickers, you’re stuck waiting or paying.
The dirty secret of “free” stock data: it’s free until it isn’t. Rate limits force you into caching strategies, stale data forces you into workarounds, and undocumented APIs force you into defensive coding. At some point, you’re spending more time on data plumbing than on actual quant work.
I’m still curious whether any provider will offer a middle-ground tier — something like 500 req/day for $291/month. The jump from “25 free requests” to “$292/month for thousands” feels steeper than it needs to be. Until then, Finnhub’s free 60/min is the best deal for anyone past the tutorial phase but not ready to commit to paid plans.
Did you find this helpful?
Your support keeps this blog running and ad-free content coming.
☕ Buy me a coffeeMost Popular Posts
- Custom Metaclass in Python: 43% Faster Validation (12,831 views)
- Python match-case: 7 Patterns That Beat if-elif Chains (954 views)
- YOLOv8 INT8 Quantization: 4x Faster on Jetson Orin (783 views)
- yfinance Alternatives 2026: 7 Free APIs Compared (732 views)
- PaddleOCR vs EasyOCR vs Tesseract: Why PaddleOCR Is Slower (570 views)