Google Trends API & Scraper — pytrends Alternative
Pricing
from $0.01 / 1,000 dataset items
Google Trends API & Scraper — pytrends Alternative
Google Trends interest over time, interest by region, and related queries (top + rising) for any keyword, country, time range or explore URL. Direct API, automatic 429 retry, and you only pay for results that contain data.
Pricing
from $0.01 / 1,000 dataset items
Rating
0.0
(0)
Developer
Daniel Posztos
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
13 days ago
Last modified
Share
Google Trends API & Scraper — a pytrends Alternative Without 429 Errors
SEO title: Google Trends API — pytrends alternative, no 429 errors SEO description: Google Trends API for Python, JavaScript or no code: interest over time, by region and related queries for any keyword. 429 rate limits handled for you; pay only for complete results.
A Google Trends API you can call from Python, JavaScript, any HTTP client or an AI agent, and a drop-in replacement for pytrends when it keeps failing with TooManyRequestsError (HTTP 429). Give it keywords (or Google Trends explore URLs) and get Google Trends data back as clean JSON, one row per query:
- Interest over time: the full 0-100 time series plus a per-term summary (average, peak and when it happened, latest value)
- Interest by region: countries, states/regions, cities (with coordinates) or US metro areas
- Related queries: top and rising searches, including "Breakout" terms
Compare up to 5 terms on one shared scale, or look up hundreds of terms one by one. Works for web, image, news, YouTube and Google Shopping search.
Why this exists
Google Trends has no official API, and scraping it is unreliable: Google answers bursts of requests with HTTP 429 "Too Many Requests". In our tests, 28 of 30 back-to-back data requests from one IP were rejected. Browser-based Trends scrapers run into this constantly, and they are slow.
This actor works differently:
- No browser. It calls the same JSON endpoints the Google Trends web page uses, and runs 3 queries in parallel.
- Automatic 429 handling. Each request goes out directly first. When Google rejects it, the actor waits and retries on fresh datacenter proxy sessions (new IP, new cookies), and as a last resort on residential IPs. Anything that still fails gets one more try at the end of the run, after a cool-down.
- You only pay for complete results. A term with no search volume, a query that still fails, or a row with a section missing still appears in the dataset (so you can see what happened), but it is not charged.
Cloud test, 40 US keywords: 39 complete results, 1 with no search volume (not charged), 0 failed, in 37 seconds. Why the retry ladder matters: in a test the same day, the shared datacenter proxy alone got throttled by Google (2 of 5 keywords failed after 42 retries), while direct requests from the same cloud kept working (5 of 5). So every request now starts direct, and proxies are only the fallback.
Input
| Field | What it does | Default |
|---|---|---|
searchTerms | Keywords, e.g. ["bitcoin", "ethereum"]. One row per term. | — |
compareTerms | Compare terms in groups of up to 5 on one shared 0-100 scale (one row per group). | false |
startUrls | Google Trends explore URLs; each keeps its own settings. | — |
geo | "" = worldwide, "US", "US-CA", "US-CA-807" (metro) | "" |
timeRange | now 1-H, now 4-H, now 1-d, now 7-d, today 1-m, today 3-m, today 12-m, today 5-y, all, or "2024-01-01 2024-12-31" | today 12-m |
category | Google Trends category ID (cat= in a Trends URL), 0 = all | 0 |
searchProperty | web, images, news, youtube, shopping | web |
include | Any of interestOverTime, interestByRegion, relatedQueries | all three |
regionResolution | COUNTRY, REGION, CITY, DMA, or empty = Google's default | "" |
language | Language of region names and queries, e.g. en-US, de, hu | en-US |
timezoneOffsetMinutes | Google's tz: minutes west of UTC (New York = 300) | 0 |
maxConcurrency | Queries running in parallel (1-5) | 3 |
proxyConfiguration | Proxy for retries: datacenter first, residential as last resort (custom groups replace both) | Apify Proxy |
You need at least one search term or one start URL.
Worked example — input
{"searchTerms": ["bitcoin"],"geo": "US","timeRange": "today 3-m"}
Worked example — output (real data, shortened with "...")
{"searchTerms": ["bitcoin"],"geo": "US","timeRange": "today 3-m","category": 0,"searchProperty": "web","trendsUrl": "https://trends.google.com/trends/explore?q=bitcoin&date=today%203-m&geo=US","summary": {"bitcoin": {"average": 53,"peakValue": 100,"peakDate": "2026-08-21T00:00:00Z","latestValue": 53,"latestDate": "2026-10-12T00:00:00Z","latestIsPartial": true}},"interestOverTime": [{"date": "2026-07-12T00:00:00Z", "timestamp": 1783814400, "formattedTime": "Jul 12, 2026", "values": {"bitcoin": 66}, "isPartial": false},{"date": "2026-07-13T00:00:00Z", "timestamp": 1783900800, "formattedTime": "Jul 13, 2026", "values": {"bitcoin": 77}, "isPartial": false},"...",{"date": "2026-10-12T00:00:00Z", "timestamp": 1791763200, "formattedTime": "Oct 12, 2026", "values": {"bitcoin": 53}, "isPartial": true}],"averages": {"bitcoin": 53},"interestByRegion": [{"geoCode": "US-KS", "geoName": "Kansas", "values": {"bitcoin": 100}},{"geoCode": "US-NV", "geoName": "Nevada", "values": {"bitcoin": 77}},{"geoCode": "US-CA", "geoName": "California", "values": {"bitcoin": 74}},"..."],"relatedQueries": {"bitcoin": {"top": [{"query": "bitcoin price", "value": 100, "link": "https://trends.google.com/trends/explore?q=bitcoin+price&date=today+3-m&geo=US"},{"query": "bitcoin stock", "value": 20, "link": "https://trends.google.com/trends/explore?q=bitcoin+stock&date=today+3-m&geo=US"},"..."],"rising": [{"query": "british man recovers lost bitcoin", "value": 12250, "formattedValue": "Breakout", "link": "https://trends.google.com/trends/explore?q=british+man+recovers+lost+bitcoin&date=today+3-m&geo=US"},{"query": "suriname", "value": 1450, "formattedValue": "+1,450%", "link": "https://trends.google.com/trends/explore?q=suriname&date=today+3-m&geo=US"},"..."]}},"hasData": true,"scrapedAt": "2026-10-12T13:56:02Z"}
Output
One dataset row per query (one term, or one group of compared terms):
| Field | Meaning |
|---|---|
searchTerms, geo, timeRange, category, searchProperty | What was queried (geo is WORLDWIDE when empty) |
trendsUrl | The same query on trends.google.com, for checking by eye |
summary | Per term: average, peakValue, peakDate, latestValue, latestDate, latestIsPartial |
interestOverTime | Points with date (UTC), timestamp, formattedTime, values per term, isPartial |
averages | Average interest per term over the period |
interestByRegion | Regions with data: geoCode, geoName, values; coordinates at city level |
relatedQueries | Per term: top and rising lists (formattedValue like +250% or Breakout) |
hasData | false means Google returned nothing (not charged) |
missingSections | Sections that kept failing; the rest of the row is there (not charged) |
error | Only present when the query, or a section of it, failed after all retries |
Rows come in input order, except queries that needed the end-of-run retry: those come last.
How to read the numbers: Google Trends values are relative, not search counts. 100 is the peak within the query, and everything else is scaled to it. Compared terms share one scale. Terms queried one by one each have their own scale, so compareTerms: true is what you want when the question is "which is searched more?". A latestIsPartial: true point covers a period that isn't over yet, so it usually reads low.
Use it from code or an AI agent
Run it synchronously and get the rows in one HTTP call:
curl -X POST "https://api.apify.com/v2/acts/westerly_breaker~google-trends-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"searchTerms": ["chatgpt", "claude", "gemini"], "compareTerms": true, "timeRange": "today 3-m"}'
From Python (pip install apify-client), the pytrends equivalents are interestOverTime (pytrends interest_over_time), interestByRegion (interest_by_region) and relatedQueries (related_queries):
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("westerly_breaker/google-trends-scraper").call(run_input={"searchTerms": ["chatgpt", "claude"], "compareTerms": True, "timeRange": "today 3-m"})for row in client.dataset(run["defaultDatasetId"]).iterate_items():print(row["searchTerms"], row["averages"])
The input fields are plain JSON with descriptive error messages. AI agents (Apify MCP server, LangChain, n8n, Make) can call the actor without a custom adapter, and if an input is wrong, the error message says what to fix.
Pricing (pay-per-event)
$0.002 per complete result. That's one row: one term, or one group of up to 5 compared terms, with every section you asked for. Platform usage is included; there is no separate compute charge.
- 100 keywords, 97 complete, 2 with no search volume, 1 failed → 97 × $0.002 = $0.194
- Rows without data, failed rows and partial rows are delivered but never charged.
- If you set a maximum cost per run, the actor stops cleanly at that limit. Every row you paid for is delivered, and you are never billed for a row you didn't get.
Error messages
Bad input stops the run before any request, with a message that names the field and the fix, for example:
'geo' 'USA' is not a valid Google Trends location code. Use '' (worldwide), an ISO 3166 country code like 'US' or 'HU', a region like 'US-CA', or a US metro (DMA) like 'US-CA-807'.Search term 'a,b' contains a comma. Put each term in its own list entry instead, and set 'compareTerms' to true if you want them compared.
Known limitations
- Related topics are not available. Google returns an empty topic list to clients that aren't a full browser, so this actor doesn't offer the section rather than return empty data. Related queries work normally.
- Values are relative (0-100), not absolute search volumes. This is how Google Trends works, not a limitation of the actor.
- Low-volume terms can come back empty. Google hides data below its privacy threshold. These rows have
hasData: falseand are free. - Per-term regional maps of compared terms are not fetched. The compared map (all terms on one scale) is. Query the terms one by one if you need each term's own regional ranking.
Data source
Public Google Trends data from trends.google.com, the same JSON the website loads. No login, no API key, no personal data.