Google Trends Scraper — cached, no timeouts avatar

Google Trends Scraper — cached, no timeouts

Pricing

Pay per event

Go to Apify Store
Google Trends Scraper — cached, no timeouts

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

Leo Nguyen

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 hours ago

Last modified

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.

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:

OutcomeRunsShare
Succeeded15,32470.5%
Timed out3,98418.3%
Failed7863.6%
Aborted by the user1,6467.6%
Total21,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

DataFieldNotes
Interest over timepoints[]date, timestamp, value (0–100), isPartial
Interest by regionregions[]geoCode, geoName, value, hasData
Related topicstop[], rising[]query, type, value, formattedValue, link
Related queriestop[], rising[]same shape
Trending nowtrends[]term, geo, startedAt, searchVolume
Comparisonpoints[].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 /explore plus one request per data type you asked for.
  • A failed keyword does not fail the run. It is returned as an item with an error field, so a 100-keyword job gives you 99 good rows instead of nothing.
  • Set cacheMaxAgeMinutes: 0 if you genuinely need a live fetch every time. You will get more 429s. That is the trade.
  • relatedQueries / relatedTopics are 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 keywords are fetched separately and their numbers are not comparable to each other. When you need comparison, use compareKeywords (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.
  • searchVolume on Trending now is Google's own rounded bucket (e.g. 100000, 1000000), not a precise count.
  • The older dailytrends / realtimetrends endpoints 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.txt
mkdir -p storage/key_value_stores/default
echo '{"keywords":["bitcoin"],"geo":"US"}' > storage/key_value_stores/default/INPUT.json
python -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')))"