Google Trends Scraper — cached, no timeouts
Pricing
Pay per event
Google Trends Scraper — cached, no timeouts
Interest over time, by region, related queries and Trending now. Built for the one thing that breaks Trends scrapers: rate limits. Caches every result, paces itself, and rotates IP instead of sleeping — so runs finish.
Pricing
Pay per event
Rating
0.0
(0)
Developer
Leo Nguyen
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
4 hours ago
Last modified
Categories
Share
Google Trends Scraper (cached, reliable)
Interest over time, interest by region, related topics/queries, and Trending now from Google Trends. No API key, no browser.
Why another Google Trends Actor
Because runs on this data source do not finish. Apify publishes every public Actor's run stats, and over the last 30 days the most-used Google Trends Actor recorded:
| Outcome | Runs | Share |
|---|---|---|
| Succeeded | 15,324 | 70.5% |
| Timed out | 3,984 | 18.3% |
| Failed | 786 | 3.6% |
| Aborted by the user | 1,646 | 7.6% |
| Total | 21,740 |
Nearly 22% of runs returned nothing — and the largest single category is timeouts, not errors. That is the signature of one thing: Google Trends rate-limiting, waited out until the clock ran out.
This Actor is built around that one problem. It caches every result and paces itself under one request per second, so the limit is approached far more slowly — and when a 429 does arrive it rotates to a fresh IP instead of sleeping, because a backoff on Apify is billed idle time that still ends in a timeout.
Being straight with you: this Actor is new, so it has no 30-day success rate of its own to show yet. What is measured below is the rate limit itself — where it triggers, and how long it lasts.
What you get
| Data | Field | Notes |
|---|---|---|
| Interest over time | points[] | date, timestamp, value (0–100), isPartial |
| Interest by region | regions[] | geoCode, geoName, value, hasData |
| Related topics | top[], rising[] | query, type, value, formattedValue, link |
| Related queries | top[], rising[] | same shape |
| Trending now | trends[] | term, geo, startedAt, searchVolume |
| Comparison | points[].values | {term: value} per point — normalised across terms |
Every item also carries fromCache (boolean) so you always know whether you got a fresh fetch
or a cached one.
The rate limit, measured
Measured against the live endpoints on 2026-08-06 from a plain datacenter IP:
- A burst with no pacing was rate-limited at request 93 in 13.9 s (~6.6 req/s). The 429 had not cleared 20 s later; it had cleared by 60 s.
- At a 1.2 s gap between requests, 30 consecutive requests passed clean.
- The budget is per IP and it accumulates: after roughly 130 paced requests spread over hours, the same address was refused on the very first request of a fresh session. So pacing alone cannot carry production volume — caching and IP rotation are not optimisations here, they are the mechanism.
So this Actor paces itself under 1 request/second by default and caches every result. Two runs asking for the same term, location and range within the cache window cost one request to Google, not two. Google Trends is weekly-resolution for most ranges, so a cached answer from an hour ago is not a worse answer — it is the same answer.
Consequences you should expect:
- A large keyword list is slow on purpose. Roughly 1.5 s per request, and each keyword costs
one
/exploreplus one request per data type you asked for. - A failed keyword does not fail the run. It is returned as an item with an
errorfield, so a 100-keyword job gives you 99 good rows instead of nothing. - Set
cacheMaxAgeMinutes: 0if you genuinely need a live fetch every time. You will get more 429s. That is the trade. relatedQueries/relatedTopicsare the first thing Google refuses. Measured: that endpoint exhausted the per-IP budget while the interest-over-time endpoint on the same session still answered. If you need related data at volume, run on a plan with more proxy IPs — the shared datacenter pool on the free plan is a handful of addresses, and this Actor can only rotate between the ones it is given.- Rotation is bounded on purpose. Each retry on a new IP still costs its pacing delay and its request time, and Apify bills compute per second — a run allowed to rotate indefinitely can spend more than its results are worth. After a set number of rotations you get an error row rather than an ever-growing bill.
Limits — read before buying
- Values are relative, not absolute. Google returns 0–100 scaled within one request. So
terms in
keywordsare fetched separately and their numbers are not comparable to each other. When you need comparison, usecompareKeywords(up to 5 terms in one request): those values ARE normalised against each other, and it is cheaper too — 3 terms cost 2 requests instead of 6. Google itself caps a comparison at 5; a 6th term is silently dropped, so this Actor refuses it rather than returning a result quietly missing a term you paid for. searchVolumeon Trending now is Google's own rounded bucket (e.g. 100000, 1000000), not a precise count.- The older
dailytrends/realtimetrendsendpoints Google retired now return 404. This Actor uses the current endpoint. If Google rotates it again, the Actor reports a clear "endpoint unavailable" error instead of returning empty results that look like real data. - Google Trends has no official public API and its Terms of Service restrict automated access. You are responsible for how you use the output.
- No personal data is collected — Trends returns aggregate search interest only.
Input
{"compareKeywords": ["bitcoin", "ethereum", "solana"],"geo": "US","timeframe": "today 12-m"}
…returns one item whose every point carries all three terms on one scale:
{ "date": "Aug 2 – 8, 2026", "values": { "bitcoin": 21, "ethereum": 3, "solana": 3 } }
Or fetch terms independently (values not comparable across terms):
{"keywords": ["bitcoin", "ethereum"],"geo": "US","timeframe": "today 12-m","dataTypes": ["interestOverTime", "relatedQueries"],"includeTrendingNow": true,"trendingGeo": "VN","cacheMaxAgeMinutes": 360}
timeframe accepts Google's own range strings: today 12-m, today 5-y, now 7-d (hourly
points), all (since 2004), or an explicit 2025-01-01 2025-12-31.
geo is a country code (US, VN, GB) or a sub-region (US-CA). Empty means worldwide.
Output
One dataset item per keyword × data type, plus one item for the trending feed:
{"keyword": "bitcoin","dataType": "interestOverTime","geo": "US","timeframe": "today 12-m","fromCache": false,"pointCount": 53,"points": [{ "date": "Aug 3 – 9, 2025", "timestamp": "1754179200", "value": 36, "isPartial": false }]}
Development
pip install -r requirements.txtmkdir -p storage/key_value_stores/defaultecho '{"keywords":["bitcoin"],"geo":"US"}' > storage/key_value_stores/default/INPUT.jsonpython -m src
src/trends.py is plain standard library and can be exercised on its own, without the Apify
SDK — useful when checking whether Google changed something:
$python -c "from src.trends import TrendsClient; c=TrendsClient(); print(list(c.explore('bitcoin','US')))"