Yahoo Finance Scraper: Prices & Fundamentals (Tested)
A yahoo finance scraper guide tested live: keyless chart endpoint for prices, crumb-auth quoteSummary for fundamentals, server-rendered pages, watch mode.

On this page
Yahoo Finance ships 892KB of HTML per quote page and documents none of the two JSON endpoints that carry its data. I re-tested every door on October 16, 2026, from a home connection: the chart endpoint returned AAPL’s market price in under a second with no key, the fundamentals endpoint refused my first request for lack of a crumb token, and the quote page turned out to be server-rendered. A static fetch took 1.9 seconds and still captured the whole statistics block. This yahoo finance scraper guide walks all three routes, with the commands and the output I actually got.
The rules, in one paragraph: Yahoo’s terms of service govern what you may do with the data, robots.txt allowed /quote/ paths to automated clients on test day while disallowing /quotes/ and a pile of internal endpoints, and none of this is legal advice; Is Web Scraping Legal? has the plain-English version. Keep request rates human. Every number below is a snapshot of a moving market, not a reference value.
Which Yahoo Finance scraper route fits your job
| Route | What you get | Needs | My test today |
|---|---|---|---|
| Chart endpoint (undocumented JSON) | Historical + intraday OHLCV | Nothing: no key, no cookie | Worked: 61 monthly bars for 5 years, keyless |
| quoteSummary (undocumented JSON) | Statements, valuation, analyst data | Cookie + crumb dance | Worked: 4 annual periods per statement |
| The HTML pages | Quote stats, income statement, profile, history | Any HTTP client | Worked statically at 1.9s per page |
| yfinance (Python library) | Both endpoints wrapped | pip install |
Worked: 5 daily bars in 0.8s on v1.7.0 |
| Portfolio, saved screeners | Your account data | Login | Not covered here |
Same tool ran the HTML route in my tests: Magpie, a local CLI whose fetch-clean-extract pipeline returns Markdown or JSON. It has no Yahoo-specific extractor (its 27 verticals cover Reddit, GitHub, YouTube and friends, but finance is a general-pages job), and that is fine, because the pages render server-side.
Route 1: the keyless Yahoo Finance API for prices
The least-known of the three doors, and the one that needs the least ceremony:
curl -s "https://query1.finance.yahoo.com/v8/finance/chart/AAPL?range=5y&interval=1mo" \
-A "Mozilla/5.0"
No key, no signup, no cookie. The response has a meta block (currency, exchange, regularMarketPrice, 52-week high/low) and an indicators.quote[0] object holding parallel open, high, low, close, volume arrays indexed by timestamp. For range=5y&interval=1mo I got 61 rows; the first close was 165.30 back in late 2021.
Pick range and interval to fit the job: 1d/1m for today’s tape, 5y/1mo for backtests, max/3mo for the full history. When you hammer it, the endpoint throttles; other people’s 429s on it are well documented, and my low-volume test never triggered one. If you want a wrapper instead of raw curl, finding a site’s hidden API covers how to identify endpoints like this one yourself.
Route 2: fundamentals via quoteSummary and the crumb dance
The deeper endpoint is v10/finance/quoteSummary/{ticker}. Ask it without credentials and you get this exact error:
{"finance":{"result":null,"error":{"code":"Unauthorized","description":"Invalid Crumb"}}}
Yahoo wants a cookie from its domain and a crumb token issued against that cookie. Both are free; the dance is three curl calls:
UA="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/126.0 Safari/537.36"
# 1. collect a cookie (the 404 is expected; it still sets one)
curl -s -c jar.txt -A "$UA" "https://fc.yahoo.com" -o /dev/null
# 2. exchange the cookie for a crumb
CRUMB=$(curl -s -b jar.txt -A "$UA" "https://query1.finance.yahoo.com/v1/test/getcrumb")
# 3. call quoteSummary with both
curl -s -b jar.txt -A "$UA" \
"https://query1.finance.yahoo.com/v10/finance/quoteSummary/AAPL?modules=price,summaryDetail,defaultKeyStatistics,financialData&crumb=$CRUMB"
My crumb looked like 3kcUiKvR/MQ and the call returned summaryDetail (previous close, day range, P/E), defaultKeyStatistics (beta, shares outstanding), and financialData (margins, returns, target prices) in one response. For the actual statements, add modules:
modules=incomeStatementHistory,cashflowStatementHistory,balanceSheetHistory
Each came back with 4 annual periods. That is the whole fundamentals job in one request, and it is what the broken statement pages (next section) refuse to give you in HTML. Two caveats: this endpoint is undocumented and Yahoo has changed its auth requirements before, and crumbs expire, so re-run the dance per session rather than caching a crumb for a month.
Route 3: scrape Yahoo Finance’s server-rendered pages
The surprise of this test: Yahoo now renders the quote page’s data on the server. A static fetch — no browser — gets the full statistics block. With Magpie:
magpie scrape "https://finance.yahoo.com/quote/AAPL/" --render static --format json
1.9 seconds wall time for Microsoft’s equivalent page, 847 words of Markdown, and the numbers were all there: price and change, open, bid/ask with size, day and 52-week ranges, volume, market cap, beta, P/E, EPS, earnings date, dividend yield, analyst price targets. A valuation-measures block and financial highlights (revenue, margins, ROE, cash, debt-to-equity) rode along in the same fetch. A quote-page fetch also returns the trailing-return comparison against the S&P 500.
The other pages split cleanly:
| Page | Static fetch result today |
|---|---|
/quote/AAPL/financials/ |
Full income statement: TTM plus 4 fiscal years of revenue, cost, profit |
/quote/AAPL/history/ |
Recent daily rows with OHLC and volume (911 words) |
/quote/AAPL/key-statistics/ |
Quarterly statistics tables (835 words) |
/quote/AAPL/profile/ |
Sector, officers with pay, corporate data |
/quote/AAPL/balance-sheet/ |
Shell, too thin to pass a content check |
/quote/AAPL/cash-flow/ |
Shell; the old cash-flow-statement/ URL 302s to the quote page |
So the income statement is scrapeable HTML while the other two statements are JS-only shells today, and a browser render did not save me: the cash-flow page kept the load event pending until timeout, twice. When a page is a shell, stop scraping HTML and take Route 2 instead: the same data sits in quoteSummary. For the tables that do render, scraping tables into structured data shows how to keep the columns straight.
One page-level note: none of the Yahoo pages I tested emitted JSON-LD structured data, so schema-based extraction has nothing to grip. You are extracting visible text and tables, which is exactly what the Markdown output preserves.
Many tickers without being a jerk
Loop politely, sleep between hits, and store per-ticker JSON:
for T in AAPL MSFT NVDA GOOG AMZN; do
magpie scrape "https://finance.yahoo.com/quote/$T/" --render static --format json \
--out "$T.json"
sleep 3
done
Then flatten the JSON to a spreadsheet: the CSV and JSON export guide walks that half. Five tickers at 3 seconds apart is under a minute. A thousand tickers at that cadence is an hour and a wiser plan: use the chart endpoint in batches, cache aggressively, and re-read scraping without getting blocked before you scale.
yfinance: the library that does the dance for you
If you live in Python, yfinance wraps both endpoints, handles the cookie-crumb work, and hands you DataFrames:
import yfinance as yf
h = yf.Ticker("AAPL").history(period="5d") # 5 rows in 0.8s today
fi = yf.Ticker("AAPL").fast_info # last price + market cap in 0.2s
That ran on yfinance 1.7.0 today. Its real problems are operational, not syntactic: when Yahoo tightens throttling, yfinance users eat 429 storms until the library ships a workaround, and the GitHub issue tracker fills up with “everything returns empty” threads. The library also only knows tickers: no news pages, no officer lists, nothing outside its endpoints. I would use yfinance for price series in a Python pipeline and the raw routes above when I need page context or a language without a Yahoo client.
Watching a ticker for price changes
For monitoring rather than bulk extraction, Magpie’s watch mode stores a snapshot of the page and diffs it on a timer:
magpie watch "https://finance.yahoo.com/quote/AAPL/" --every 30s \
--webhook "https://your-endpoint.example/hook"
During pre-market hours today, the first 30-second cycle caught a real change and printed a word-diff: 345.80 +3.80 (+1.11%) became 345.57 +3.57 (+1.04%), and the timestamp moved from 8:13:21 to 8:14:15. With --webhook, one POST carrying the diff goes out per change; with --once it is a single check you can run from cron. Two honest warnings: the diff fires on any content change, so a reordered news module looks like a price move, and in my capture Yahoo’s own label flip-flopped between “Nasdaq Real Time Price” and “Delayed Quote” between checks. Filter diffs on the price line before you page anyone.
What this toolchain cannot do here
Saying it plainly, since the plan for this page says Magpie fits:
- No hosted API. Magpie is a local binary (AGPL-3.0-or-later or commercial) plus a desktop app; nothing runs in a cloud on your behalf, and there is no Yahoo-specific vertical to type results for you.
- It does not defeat throttling. When Yahoo rate-limits your IP, the correct response is slower requests and caching, not retry storms.
- The shell pages beat it. Balance-sheet and cash-flow HTML would not render statically or in a browser on test day; quoteSummary is the route for those, via curl or yfinance.
- Login-only surfaces (portfolios, saved screeners) are out of scope for every route in this guide.
FAQ
Is it legal to scrape Yahoo Finance?
Public quote and filing data sits behind Yahoo’s terms of service, which restrict automated collection; people scrape small volumes anyway and the practical risk is enforcement against your IP or account. Respect robots.txt, throttle, and get advice before commercial use.
Does Yahoo Finance have an official API?
Not a documented public one. The chart and quoteSummary endpoints are undocumented, keyless or crumb-gated, and changeable without notice. Yahoo sells direct data access to institutions under license, which is the paid, signed contract most articles are actually thinking of. The commercial scraping-API vendors (Bright Data and friends) resell Yahoo data on their own terms if volume pushes you that way.
Why is yfinance returning empty data or 429s?
Yahoo throttles aggressive clients, and during tightening waves whole IP ranges get empty responses until yfinance ships a fix. Update the library first, then slow down, cache responses, and reduce request volume before blaming your code.
Can I get fundamentals without a Yahoo login?
Yes. quoteSummary with a fresh cookie and crumb returns statements, valuation, and key statistics for any ticker. Login only matters for personal portfolios and saved screeners, which you should not scrape anyway; use Yahoo’s export where it exists.
How current is the data?
Intraday quotes update continuously and the chart endpoint’s meta carries the last trade time. My watch diff showed Yahoo alternating its own label between real-time and delayed within minutes, so treat any single scrape as one sample, not the tape.
Pick by job
Backtesting or a price history: chart endpoint, keyless, done in one curl. Fundamentals across many tickers: the crumb dance against quoteSummary, or yfinance if Python is already your home. Page context (news, officers, the quote block as a document): a static fetch at 2 seconds a page, watched with --every 30s if the number itself is what you care about. And when a page comes back a shell, believe it and switch to the JSON route instead of escalating browsers at it.


