What the Google Finance scraper collects
Scrapes Google Finance in seven modes: instrument quotes, quarterly financials, analyst ratings and price targets, OHLCV bars, the market page's index, sector, currency, crypto and futures strips, the day's movers and the earnings calendar. Every mode reads the page's own server-rendered payload from the first response, so there is no scrolling or hydration wait. Output is JSON or CSV, one row class per kind of data.
Quotes
Input: One or more symbols (TICKER:EXCHANGE or BASE-QUOTE)One row per instrument: price, previous close, open/high/low, volume, market cap, industry and extended-hours price.
Market strips
Input: A market code, optionally one stripEvery strip the market page publishes: indices, sectors, currencies, crypto and futures, geo-selected by --market.
Movers
Input: A market codeThe gainers, losers and most-active lists the market page previews (top 1 to 4 of each).
Quarterly financials
Input: One or more symbolsOne row per reporting period: revenue, net income, operating expense, EBITDA, EPS, net margin, effective tax rate and the street estimate.
Analyst ratings
Input: One or more symbolsThe consensus, buy/hold/sell split and 12-month target range, plus each published action with firm, date, rating and price target.
OHLCV chart
Input: One or more symbolsEvery bar the page carries: the latest session at 5-minute resolution plus about a month of daily bars, from one fetch.
Earnings calendar
Input: A market codeUpcoming earnings announcements with revenue and EPS estimates.
From zero to Google Finance data in 3 steps
Install
Clone the repository and install one browser engine. Python 3.9+.
$ git clone https://github.com/2scraper/google-finance-scraper $ cd google-finance-scraper $ python3 -m venv .venv $ ./.venv/bin/pip install -r requirements.txt -r requirements-playwright.txt $ ./.venv/bin/playwright install chromium
Run a scraper
Pick what you need and run it. Every flag is documented in the README.
$ python3 playwright_scraper.py --mode quote --symbols GOOGL:NASDAQ,BMW:ETR,EUR-USD --out quotes$ python3 playwright_scraper.py --mode markets --market US --out markets$ python3 playwright_scraper.py --mode movers --market GB --out movers$ python3 playwright_scraper.py --mode financials --symbols GOOGL:NASDAQ$ python3 playwright_scraper.py --mode analysts --symbols GOOGL:NASDAQ$ python3 playwright_scraper.py --mode chart --symbols GOOGL:NASDAQ,BMW:ETR$ python3 playwright_scraper.py --mode earnings --market USGet structured data
Rows land in JSON or CSV (
--format), with a.meta.jsonsidecar recording status, pages fetched and why the run stopped.
Google Finance data fields
32 columns per row in a predictable schema. The most useful ones:
| Field | What it holds |
|---|---|
sku | Google's own canonical symbol, e.g. GOOGL:NASDAQ |
title | Instrument name |
price | Last price |
currency | Quote currency; null on an index |
instrument_type | stock, index, currency, crypto, futures… |
prev_close | Previous close |
open | Session open |
day_high | Session high |
day_low | Session low |
change_pct | Change against the previous close, in percent |
volume | Volume; null, never 0, where Google publishes none |
market_cap | Market capitalisation; null, never 0, where there is none |
industry | Industry |
quoted_at | Google's as-of time for the quote |
after_hours_price | Extended-hours price |
market | The market (gl) the row was served for |
[
{
"source": "google.com/finance",
"scraped_at": "2026-09-22T09:14:42.176201+00:00",
"url": "https://www.google.com/finance/beta/quote/.DJI:INDEXDJX",
"sku": ".DJI:INDEXDJX",
"title": "Dow Jones Industrial Average",
"price": 52048.83,
"currency": null,
"ticker": ".DJI",
"exchange": "INDEXDJX",
"instrument_type": "index",
"prev_close": 51682.64,
"open": null,
"day_high": null,
"day_low": null,
"change": 366.1875,
"change_pct": 0.70853096,
"volume": null,
"market_cap": null,
"industry": null,
"country": null,
"timezone": "America/New_York",
"utc_offset": -14400,
"quoted_at": "2026-09-21T20:50:57+00:00",
"after_hours_price": null,
"after_hours_change": null,
"after_hours_change_pct": null,
"listing": "index",
"market": "US",
"entity_id": "/m/0cqyw",
"price_source": "af_list",
"page": 1,
"position": 1
},
{
"source": "google.com/finance",
"scraped_at": "2026-09-22T09:14:42.176220+00:00",
"url": "https://www.google.com/finance/beta/quote/.INX:INDEXSP",
"sku": ".INX:INDEXSP",
"title": "S&P 500",
"price": 7764.7,
"currency": null,
"ticker": ".INX",
"exchange": "INDEXSP",
"instrument_type": "index",
"prev_close": 7650.5,
"open": null,
"day_high": null,
"day_low": null,
"change": 114.200195,
"change_pct": 1.4927155,
"volume": null,
"market_cap": null,
"industry": null,
"country": null,
"timezone": "America/New_York",
"utc_offset": -14400,
"quoted_at": null,
"after_hours_price": null,
"after_hours_change": null,
"after_hours_change_pct": null,
"listing": "index",
"market": "US",
"entity_id": "/m/016yss",
"price_source": "af_list",
"page": 1,
"position": 2
}
]First 2 of 46 rows from a real run — sample_output.json. Long values shortened here.
Handles what breaks naive Google Finance scrapers
Parsed by shape, not by ds:N key
The payload's ds:N key numbers shift when a query parameter changes the page. Records are found by their shape, and the suite renumbers every key in a real fixture to prove no row changes.
Zero is not a market cap
Google writes 0 where there is no volume or market cap, on every index, currency, crypto and futures row measured. Both columns are nulled so averages stay honest.
Region refusals reported as blocks
From an unsupported region Google answers 403 "not supported in your region". The scraper reports region_unavailable with exit 3 and advice about the exit country, not an empty market.
Wrong symbol spelling caught
EURUSD:CURRENCY returns HTTP 200 with Google's Page Not Found. The scraper reports not_found with the symbol named instead of an empty result.
Scraping infrastructure included
The same tested core runs every 2scraper repository, so the hard parts work the same way on every site.
CAPTCHA handling
Detects the challenge a page really serves and, with a 2Captcha key, solves it only when it blocks the data — a capped number of solves per page.
Proxy rotation
Single proxy or a pool from a file, rotated on blocks with a fresh browser per exit. Credentials stay out of argv and logs.
Fingerprints
Optional complete device identity from the 2Captcha Fingerprint API — user agent, client hints, locale and timezone that agree with each other.
Honest run metadata
Every run writes a .meta.json sidecar and distinct exit codes: complete, blocked, empty and partial are never confused, and a failed run never overwrites good data.
Diff two runs
diff_runs.py compares two complete Google Finance snapshots by id — new, removed and changed rows for monitoring.
What teams do with Google Finance data
Portfolio and watchlist tracking
Pull quotes for a mixed list of stocks, indices, FX pairs and crypto on a schedule into one table.
Fundamentals research
Collect up to two decades of quarterly revenue, net income, EBITDA and EPS against street estimates.
Analyst sentiment monitoring
Track consensus ratings, target ranges and each new analyst action per ticker.
Earnings-season planning
Read the upcoming earnings calendar per market with revenue and EPS estimates.
Free to run. Pay only for what the site requires.
Runs with no key, proxy or account
No key, no account and no proxy from a region Google serves Finance in: every mode was measured from a datacentre address with no credential on 2026-09-22. The market you read is a query parameter (--market), not your exit address. From a refused region (Russia was refused, measured Sep 2026) you need an exit in a supported country via a residential proxy or the Scraping Browser API's country segment. Captcha solving stays wired for Google's /sorry/ reCAPTCHA page, which was not met in testing.
Optional 2Captcha services — captcha solving, the Scraping Browser API, residential proxies and fingerprints — work behind one key.
Good to know
- Movers is a preview of 1 to 4 rows per list, not a full ranking.
- Financials exposes seven identified figures per quarter, not a full statement; EPS is null where Google publishes none.
- Chart depth is fixed at one intraday session plus about a month of daily bars.
2scraper vs. hosted scraper APIs
| 2scraper | Typical hosted API | |
|---|---|---|
| Code | Open source, MIT — read and patch it | Closed |
| Cost | Free; optional services billed only when used | Subscription or per request / per result |
| Runs on | Your machine, server or CI | The vendor's infrastructure |
| Output | JSON/CSV files + run metadata | API responses |
| When the site changes | Fix it yourself or open an issue / PR | Wait for the vendor |
Google Finance scraper FAQ
Do I need a proxy to read the US market from Europe?
No. --market US selects the US market from any supported region. You need an exit only if your own region is refused by Google Finance.
How do I write symbols?
Equities, indices and futures as TICKER:EXCHANGE (GOOGL:NASDAQ, .INX:INDEXSP); currency pairs and crypto as BASE-QUOTE (EUR-USD, BTC-USD).
Does movers give a full top-50 ranking?
No. Google retired its standalone gainers and losers pages; the market page publishes the top 1 to 4 of each list, and that is what the scraper returns.
How much chart history do I get?
One full session at 5-minute resolution plus about a month of daily bars. Deeper history comes from a client-side endpoint this repo does not implement, and --window does not change it.
Is there pagination?
No. A quote page is one instrument and the market page carries its lists in full, so --pages above 1 is refused; --concurrency splits symbols instead.
Is scraping Google Finance legal?
It reads public pages. Follow the site's terms and the law where you operate.