Google Trends Scraper — Interest Over Time & By Region avatar

Google Trends Scraper — Interest Over Time & By Region

Pricing

$0.05 / 1,000 per interest row returneds

Go to Apify Store
Google Trends Scraper — Interest Over Time & By Region

Google Trends Scraper — Interest Over Time & By Region

Scrape Google Trends search interest as flat, spreadsheet-ready rows: one per keyword and date, one per keyword and region. Any country or subregion, any timeframe from the last hour back to 2004, and web / news / YouTube / image / shopping search. No browser, no API key, no login.

Pricing

$0.05 / 1,000 per interest row returneds

Rating

0.0

(0)

Developer

Scrapers Delight

Scrapers Delight

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Search interest from Google Trends, delivered flat: one row per search term and date, and one row per search term and region. Load it straight into a spreadsheet, a BI tool or a database — no unnesting, no JSON surgery.

Why this one

Every other Google Trends actor hands you one item per keyword with the raw Google JSON nested inside it. Export that to CSV and the whole 12-month timeline arrives as an unusable blob in a single cell.

This one flattens it. One keyword over the past 12 months = 53 tidy rows, each with an ISO date, the 0-100 interest value, and the series average alongside it so you can see at a glance whether a week was above or below par.

It also runs on plain HTTP, no browser. Measured 2026-09-02 on a live Apify run: 3 keywords × 2 countries, both slices, 483 rows in 18.8 seconds and 24 HTTP requests. For comparison, Apify's own google-trends-scraper runs a headless browser at 4 GB, and its public 30-day run scoreboard on the same day read 5,348 TIMED-OUT and 442 FAILED out of 20,571 runs — 28% of runs did not succeed.

Quick start

{
"searchTerms": ["bitcoin", "ethereum", "solana"],
"geos": ["US", "GB"],
"timeframe": "today 12-m",
"includeInterestOverTime": true,
"includeInterestByRegion": true
}

Every term is scraped once per location, so 3 terms × 2 countries = 6 queries.

Output

Two row shapes in one dataset, told apart by dataType. 27 fields, same columns on every row so a CSV export stays rectangular.

interest_over_time — the line chart, flattened

{
"searchTerm": "ethereum",
"searchTermType": "Search term",
"dataType": "interest_over_time",
"geo": "US",
"timeframe": "today 12-m",
"timeframeLabel": "Past 12 months",
"timeRange": "2025-09-02 2026-09-02",
"granularity": "WEEK",
"category": 0,
"searchProperty": "web",
"date": "2025-08-31T00:00:00.000Z",
"timestamp": 1756598400,
"formattedTime": "Aug 31 – Sep 6, 2025",
"value": 74,
"formattedValue": "74",
"hasData": true,
"isPartial": false,
"seriesAverage": 58.8,
"comparisonGroup": null,
"trendsUrl": "https://trends.google.com/trends/explore?date=today+12-m&q=ethereum&geo=US&hl=en-US",
"scrapedAt": "2026-09-02T04:41:50.757Z"
}

interest_by_region — the map, flattened

{
"searchTerm": "ethereum",
"dataType": "interest_by_region",
"geo": "US",
"rank": 1,
"regionCode": "US-WY",
"regionName": "Wyoming",
"regionLevel": "subregion",
"value": 100,
"formattedValue": "100",
"hasData": true,
"latitude": null,
"longitude": null,
"trendsUrl": "https://trends.google.com/trends/explore?date=today+12-m&q=ethereum&geo=US&hl=en-US"
}

Measured field fill

From the live Apify run above (483 rows, 3 terms × 2 countries):

Row typenFields at 100% fill
interest_over_time318all 21 applicable fields — date, timestamp, formattedTime, value, formattedValue, hasData, isPartial, seriesAverage, granularity, timeRange, timeframeLabel, geo, searchTerm, searchTermType, dataType, timeframe, category, searchProperty, trendsUrl, scrapedAt
interest_by_region165all 19 applicable fields — regionCode, regionName, regionLevel, rank, value, formattedValue, hasData, plus the same shared columns

The offline regression suite replays 33 captured Google Trends responses across 11 query shapes (worldwide, country, US state, custom date range, hourly, per-minute, YouTube, News, a finance category, forced City detail, and a 3-way comparison) through this exact parser: 1,456 rows, 0 duplicates, 0 dropped records, every integrity assertion passing.

Fields that are null by design, not by failure:

FieldWhere it fillsWhy
date / timestamp / formattedTime / granularity / seriesAverageover-time rows onlya region has no date
regionCode / regionName / regionLevel / rankregion rows onlya date has no region
latitude / longitudeCity detail only (2% of region rows)Google returns coordinates instead of a region code for cities
regionCode98% of region rowscity rows have coordinates, not a code
geo"" means Worldwidean empty string is the real Google Trends value for worldwide
comparisonGroupcomparison mode onlylists the terms a row was normalised against

The value is 0-100, relative, and scaled to the peak of whatever you asked for. It is not a search volume.

  • By default each term is scraped on its own, so its 100 is its own busiest period. Term A's 80 and term B's 80 are not comparable.
  • Set compareKeywords: true and up to 5 terms go into ONE Google query, scaled to a single shared peak — then they are directly comparable ("brand A ran at 3× brand B all year"). Rows from a comparison carry the group in comparisonGroup.

Two more things the raw API will bite you with, both surfaced as fields here:

  • isPartial: true marks the final period, which is still being collected. It almost always looks like a crash. Filter it out before you charting a trend line.
  • hasData: false means Google has no data for that point — not an interest of zero.

What you can control

InputWhat it does
searchTermsThe keywords. Each is one query per location.
geos"" = Worldwide, US / GB / DE = country, US-CA / GB-ENG = state or region. Pass several.
timeframePast hour → 2004-present. Google picks the granularity: per minute, per 8 minutes, hourly, daily, weekly or monthly.
customTimeRange2024-01-01 2024-12-31 — any window back to 2004.
categoryA Google Trends topic ID (7 Finance, 45 Health, 174 Autos…) to strip out same-spelling searches from other subjects.
searchPropertyWeb, Images, News, YouTube or Google Shopping — the same term measured on a different Google surface.
regionResolutionAutomatic, or force Metro / City detail.
compareKeywordsScale up to 5 terms against a single shared peak.
includeRegionsWithNoDataKeep the blank regions Google pads its map with (see below).
maxItemsHard row cap; the run stops cleanly at it.
maxConcurrencyQueries in flight. Default 2 — see rate limits.

Blank regions are dropped, and not charged

Google pads the region list with entries it explicitly flags as having no data. Measured 2026-09-02: a worldwide query returned 250 countries of which 54 carried data, and forced City detail returned 200 cities of which 10 did. Shipping — and billing you for — 190 blank city rows is not a result set, so they are dropped by default and the count is reported in the run status. includeRegionsWithNoData puts them back if you are drawing a map that needs every shape.

Pricing

Pay per row. Nothing else — no run fee, no platform-usage surcharge on top.

EventPriceWhen it fires
trend-row$0.00005One per interest row delivered to your dataset

That is $0.05 per 1,000 rows. A full 12-month weekly series for one keyword is 53 rows = $0.0027; add interest by region for a US query and it is 104 rows = $0.0052. Tracking 500 keywords weekly, time series only, costs about $1.33 a month.

Rows are billed through Apify's gated pushData, so at a spend cap the run stops cleanly and you are never charged for rows you did not receive.

Rate limits — the honest version

Google Trends rate-limits hard, and this is the only real difficulty in the lane. Measured 2026-09-02 over 20 keywords (US, past 12 months):

SetupFirst-attempt success
Fresh session per query, 1 at a time18 / 20
One shared session, 1 at a time15 / 20
Fresh session per query, 4 at a time8 / 20

So the actor takes a fresh cookie jar and a fresh proxy session for every query, and again for every retry, backs off exponentially, and retries a rate-limited query up to four times. On the live 6-query Apify run above it absorbed zero 429s across 24 requests.

If a query is still rate-limited after all four attempts it is counted and named in the run status message as RATE-LIMITED, never silently reported as "this keyword has no data". Re-run those terms, or lower maxConcurrency.

Apify Proxy is on by default and should stay on — the session rotation is the entire defence. Any proxy group works; no residential requirement was measured (the same call returned 200 through Apify's automatic, datacenter, static-US and residential pools, and from an unproxied home IP).

Notes / honest limits

  • No login, no API key, no browser. Two HTTP hops per query against Google's own public Trends endpoints. A cookie-minting warm request comes first — without it Google answers 429 from every IP, which is what makes this lane look harder than it is.
  • Related queries and related topics are not in this actor. They are a different job (keyword ideation) with a different row shape, and mixing them into this dataset would wreck the flat CSV. They belong in a sibling actor.
  • This is relative interest, not search volume. Google does not publish absolute query counts anywhere, and any tool claiming to give you them from Trends is modelling, not measuring.
  • Google re-samples. Trends is built from a sample of searches, so the same query re-run tomorrow can differ by a point or two. That is Google, not the scraper.
  • now 1-H / now 4-H data has a short shelf life — Google keeps the per-minute series for only the recent window, so schedule those runs rather than backfilling them.
  • Sub-country locations only go so deep. A country gives states/provinces, a US state gives metro areas, and City detail returns Google's top 200 cities — most of which are usually blank (see above).