DEV Community

Otter Fetch
Otter Fetch

Posted on

pytrends alternative: getting Google Trends data in Python without 429 errors

If you've ever scraped Google Trends with pytrends, you've probably seen this:

pytrends.exceptions.TooManyRequestsError: The request failed: Google returned a response with code 429
Enter fullscreen mode Exit fullscreen mode

The usual fix is more time.sleep(), then proxies, then retries. It works for a while and breaks again.

Two things make it worse now. pytrends was archived in April 2025, so that 429 isn't getting fixed upstream. And the official Google Trends API that Google announced in July 2025 is still an invite-only alpha.

I build data tools under the name Otter Fetch, and Google Trends was the first one I made, exactly because of this. It's a Google Trends scraper on Apify: Google Trends Scraper: Compare & Bulk. It's my tool, so weigh my opinion accordingly. Below is how to use it from Python as a pytrends replacement.

Setup

You need an Apify account (free, no card) and your API token from Settings > API & Integrations. Then:

pip install apify-client
Enter fullscreen mode Exit fullscreen mode

Google Trends interest over time in Python

from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("otterfetch/google-trends").call(run_input={
    "searchTerms": ["air fryer"],
    "geo": "US",
    "timeRange": "past_5_years",
})

for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if row["type"] == "summary":
        print(row["keyword"], row["trend"], row["peak_date"])
Enter fullscreen mode Exit fullscreen mode

This is the same data pytrends' interest_over_time() gives you: one row per date, values from 0 to 100.

Each keyword also gets a summary row first. Most of the time the question is just "is this growing, and when did it peak", so the summary has the average, peak value and date, latest value, percent change, and a plain up / down / flat. No pandas needed for that part.

Compare keywords in Google Trends

This trips a lot of people up. Google Trends scores each keyword from 0 to 100 against its own peak. Pull "air fryer" and "slow cooker" in two separate requests and the numbers can't be compared. They have to go in the same request.

run = client.actor("otterfetch/google-trends").call(run_input={
    "searchTerms": ["air fryer", "slow cooker", "rice cooker"],
    "mode": "compare",
    "geo": "GB",
    "timeRange": "past_12_months",
    "includeRegions": True,
})
Enter fullscreen mode Exit fullscreen mode

All three share one scale. includeRegions adds interest by region, the equivalent of pytrends' interest_by_region().

Compare more than 5 keywords on Google Trends

Google Trends only compares 5 terms at a time. That's fine for a quick check and useless if you have a keyword list of a few hundred.

The known workaround is to keep one anchor keyword in every batch of 5 and rescale everything against it. Doing that by hand is fiddly, so bulk mode does it for you:

run = client.actor("otterfetch/google-trends").call(run_input={
    "importUrl": "https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID/edit",
    "mode": "bulk",
    "timeRange": "past_90_days",
    "maxKeywords": 500,
})
Enter fullscreen mode Exit fullscreen mode

Give it a Google Sheet or CSV with one keyword per row. It picks the anchor itself. Keywords too small to measure properly are marked low_precision instead of coming back as silent zeros.

If you'd rather do the anchor trick yourself, the core of it is small. Pull two batches that both contain the same anchor keyword, then rescale the second batch by the anchor's ratio:

import pandas as pd

# batch_a and batch_b: two Trends pulls (date index, one column per keyword),
# both including the anchor "coffee"
ratio = batch_a["coffee"].mean() / batch_b["coffee"].mean()
batch_b_scaled = batch_b.drop(columns="coffee") * ratio

combined = pd.concat([batch_a, batch_b_scaled], axis=1)
Enter fullscreen mode Exit fullscreen mode

The catch is that Trends rounds to whole numbers, so a keyword far smaller than the anchor ends up as a column of 0s and 1s. Pick an anchor close in size to your keywords, or chain through a middle-sized one.

Google Trends related queries

run_input = {
    "searchTerms": ["air fryer"],
    "geo": "US",
    "includeRelatedQueries": True,
}
Enter fullscreen mode Exit fullscreen mode

This returns the top and rising related queries, like pytrends' related_queries(). Rising is where new content ideas tend to show up.

One thing to watch: Google sometimes pads the rising list with queries that have nothing to do with your keyword ("hotel booking" next to "air fryer", for example). When a list looks like that, the actor marks those rows suspect_decoy and doesn't charge for them, so filter on status == "success" and you'll only keep the real ones.

How much it costs

It's pay per result: $0.002 per keyword, plus $0.001 each for regions or related queries. No start fee. Keywords where Google returns nothing, or where the data looks wrong, aren't charged.

100 keywords with everything on is about 40 cents. Apify's free plan includes $5 of credit a month, so testing it costs nothing.

Caveats

It's relative interest, not search volume. Same as the Trends website: 100 means the peak for that place and time range.

Very niche keywords can come back as no_data. A bigger region or longer time range usually helps.

It doesn't cover "trending now" or related topics, only interest over time, regions and related queries.

Without code

The actor page has a form too. Type keywords, hit Start, download a CSV or Excel file.

If something breaks or you need a feature, open an issue there. I read all of them.

What are you using Google Trends data for? Let me know in the comments.


I wrote this with help from an AI assistant, then checked the code and facts myself.

Top comments (0)