Google Trends Scraper avatar

Google Trends Scraper

Pricing

$0.80 / 1,000 result rows

Go to Apify Store
Google Trends Scraper

Google Trends Scraper

Interest over time, interest by region and related queries from Google Trends. Handles Google's 429 rate limit with cookie warm-up and proxy rotation, so runs finish instead of hanging until the platform timeout.

Pricing

$0.80 / 1,000 result rows

Rating

0.0

(0)

Developer

SellerKit

SellerKit

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

19 hours ago

Last modified

Categories

Share

Interest over time, interest by region and related queries from Google Trends.

The point of this one is that the run ends. Google answers an un-warmed request with 429, and it rate limits per address. A scraper that does not handle both does not fail quickly, it hangs, and the run gets killed at the platform timeout after you have already paid for the compute.

What comes back

dataTypeFieldsRows per term
interestOverTimedate, timestamp, value (0-100), isPartial53 for 12 months, 93 for 90 days
interestByRegionregion, regionCode, value (0-100)51 for the US, up to ~250 worldwide
relatedQueriesquery, value, formattedValue, isBreakout, linkup to 50

Every row also carries searchTerm, geo, timeframe and category, so a multi-term run is one flat table you can filter rather than a nested blob.

value is Google's own 0-100 index, not a search volume. 100 is the peak of the range you asked for, and the numbers are only comparable inside a single term and timeframe.

Google currently returns an empty list for the related-topics widget, whether the term is sent as text or resolved to a topic entity first. Rather than list it as a feature and hand you nothing, it is left out. If Google starts answering it again, it will appear as an option.

Reliability

This is the part that is different, so here is exactly what it does.

A cookie is fetched first. trends.google.com sets an NID cookie on the HTML page, and the API refuses requests that do not carry it. Warm-up happens automatically and again after any rejection.

Retries change address, not just wait. Backing off on a rate-limited address only spends time. From the second attempt each retry asks the proxy for a new session and re-warms the cookie for it.

Use the Apify proxy. This is not a formality: during development a single address that had made a few hundred requests was refused with 429 continuously for thirty minutes, checked every 45 seconds. No backoff schedule survives that. Without a proxy the retries have nothing to rotate to, and every term will eventually come back exhausted.

Every term has a deadline. secondsPerTerm is a hard budget. When it passes, the term is recorded as skipped and the run moves to the next one. This is what stops one difficult term from consuming the whole run.

One bad term does not kill the run. Terms are fetched independently. Nineteen good terms and one Google will not answer gives you nineteen terms of data and a line in the summary, not a failed run.

Partial results are labelled as partial. If the time series came back but the regional breakdown did not, that term is reported under partial with the reason. A short row count is never left looking like a small trend.

You are not charged for nothing. Billing is per row that reaches the dataset. A term that returns no data costs nothing.

The run summary

Every run writes RUN_SUMMARY to the key-value store:

{
"requested": 3,
"collected": 2,
"rows": 372,
"partial": [],
"skipped": [{ "term": "zzzz-nonexistent-term-xyz", "reason": "empty-from-google" }]
}

The reason separates "Google has nothing to say about this" from "we could not get an answer", which is the distinction that matters when you are deciding whether to re-run:

reasonMeaningWorth retrying
empty-from-googleGoogle answered, with nothing in it. Usually a term with no measurable interest in that region or timeframe.No
not-offered-by-googleGoogle did not offer this widget for this term at all.No
bad-requestGoogle rejected the term, geo or timeframe as invalid.No, fix the input
deadlineThe secondsPerTerm budget ran out.Yes, with a longer budget
exhaustedRate limited past the retry count.Yes, with a proxy enabled

If a scheduled run needs to alert on trouble, watch for deadline and exhausted in skipped and partial. The other three are Google telling you something true about the term, not a failure of the run.

A run where nothing at all came back fails loudly rather than writing an empty dataset quietly.

Input

{
"searchTerms": ["bitcoin", "ethereum"],
"geo": "US",
"timeframe": "today 12-m",
"outputs": ["interestOverTime", "interestByRegion", "relatedQueries"],
"category": 0,
"language": "en-US",
"secondsPerTerm": 120,
"proxyConfiguration": { "useApifyProxy": true }
}

geo takes a country code such as US, GB, KR, or a sub-region such as US-CA. Leave it empty for worldwide.

timeframe decides the bucket size: today 12-m gives weekly points, today 3-m gives daily, now 7-d gives hourly, all goes back to 2004 monthly.

category is a Google Trends category id. 0 is everything, 7 is Finance, 71 is Food & Drink, 5 is Computers & Electronics.

Notes

Google Trends is an index, not a dataset with an SLA. Values for the most recent bucket move as Google finishes counting, which is what isPartial marks. If you are storing history, re-fetch the tail rather than trusting the last point.