Arcadium Data Scrapers
Home / Tutorials / Google Trends without pytrends 429

Google Trends data without pytrends 429 errors

pytrends reads Google Trends from your own IP address. When Google decides that address sends too many requests, it answers HTTP 429 and pytrends raises TooManyRequestsError ("The request failed: Google returned a response with code 429"). Its issue tracker has many reports of this, for example #561, #573, #614 and #625. The repository is archived on GitHub (read-only), and its README asks for new maintainers.

arcadium/google-trends runs in the Apify cloud instead of on your machine. It reads the same public Google Trends data through Apify proxies, retries a refused keyword on a fresh connection, and returns one JSON row per keyword: interest over time, interest by region, related queries and related topics. Your code makes one call to the Apify API and never talks to Google itself. Google can still refuse requests on a bad day; then a keyword takes longer, and if every retry fails it comes back as a free row with code: "blocked" while the other keywords still arrive.

Cost of this example on the Apify Free plan: 2 × $0.0027 = $0.0054 ($0.00054 on the Apify Gold plan and higher), plus $0.00005 per GB of memory when the run starts.

What you need

Step 1: Run it in the Apify Store (no code)

  1. Open arcadium/google-trends on the Apify Store and sign in (a free Apify account works).
  2. Enter the keywords in Search terms, one per line (the kw_list you passed to pytrends).
  3. Turn on Compare keywords on one scale, which is what one pytrends build_payload call gives.
  4. Set Location to a country code such as US (pytrends geo), or leave it empty for worldwide.
  5. Optional: set a maximum cost per run in the run options as a hard cap.
  6. Click Start. Rows appear in the Output tab; export them as JSON, CSV or Excel.

The same input as JSON (paste it in the JSON tab of the input form):

{
  "searchTerms": [
    "python",
    "javascript"
  ],
  "compareKeywords": true,
  "geo": "US",
  "includeRelatedQueries": true,
  "includeRelatedTopics": false,
  "includeGeo": false
}

Step 2: Call it over the Apify API

One HTTP call runs the actor and returns its rows. It suits runs up to about five minutes; for longer runs start the run asynchronously and read the dataset when it finishes.

curl -X POST "https://api.apify.com/v2/acts/arcadium~google-trends/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchTerms":["python","javascript"],"compareKeywords":true,"geo":"US","includeRelatedQueries":true,"includeRelatedTopics":false,"includeGeo":false}'

Step 3: Run it from Python

With apify-client 3.x, call() returns a Run object: read the dataset id as run.default_dataset_id (older examples use run["defaultDatasetId"], which no longer works).

# pip install "apify-client>=3" pandas
import os

import pandas as pd
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
# pytrends: build_payload(["python", "javascript"], geo="US"), then interest_over_time()
run = client.actor("arcadium/google-trends").call(run_input={
    "searchTerms": ["python", "javascript"],
    "compareKeywords": True,
    "geo": "US",
    "includeRelatedQueries": True,
    "includeRelatedTopics": False,
    "includeGeo": False,
})
series, rising = {}, {}
# apify-client 3.x returns a Run object: use run.default_dataset_id, not run["defaultDatasetId"]
for row in client.dataset(run.default_dataset_id).iterate_items():
    if row.get("rowType") != "trend":
        print("not delivered:", row.get("code"), row.get("message"))  # free row, e.g. code "blocked"
        continue
    points = row.get("interestOverTime_timelineData") or []
    series[row["searchTerm"]] = pd.Series(
        [p["value"][0] for p in points],
        index=pd.to_datetime([int(p["time"]) for p in points], unit="s"),
    )
    rising[row["searchTerm"]] = [q["query"] for q in row.get("relatedQueries_rising") or []]

# Same shape as pytrends interest_over_time(): one column per keyword, 0-100 on one scale
df = pd.DataFrame(series)
print(df.tail())
print(rising)

Step 4: Run it from JavaScript

// npm install apify-client
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('arcadium/google-trends').call({
  "searchTerms": [
    "python",
    "javascript"
  ],
  "compareKeywords": true,
  "geo": "US",
  "includeRelatedQueries": true,
  "includeRelatedTopics": false,
  "includeGeo": false
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);

Step 5: Ask an AI assistant (MCP)

Apify’s hosted MCP server turns the actor into one tool. Add it to Claude, Cursor, VS Code or any MCP client:

{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/?tools=arcadium/google-trends",
      "headers": {
        "Authorization": "Bearer <APIFY_TOKEN>"
      }
    }
  }
}

Then ask, for example:

Use arcadium/google-trends to compare US search interest in "python" and "javascript" over the past 12 months on one scale, and list the rising related queries of each.

More clients and the local setup on the MCP page.

Plain HTTP from Python (no client library)

One POST to the run-sync-get-dataset-items endpoint starts the run, waits for it and returns the rows as a JSON array. It suits runs up to about five minutes; for long keyword lists use apify-client as above, which waits as long as the run takes.

# pip install requests
import os

import requests

resp = requests.post(
    "https://api.apify.com/v2/acts/arcadium~google-trends/run-sync-get-dataset-items",
    headers={"Authorization": "Bearer " + os.environ["APIFY_TOKEN"]},
    json={"searchTerms": ["python", "javascript"], "compareKeywords": True, "geo": "US"},
    timeout=330,
)
resp.raise_for_status()
for row in resp.json():
    if row.get("rowType") == "trend":
        print(row["searchTerm"], row["averageInterest"])
    else:
        print("not delivered:", row.get("code"), row.get("message"))

From pytrends calls to actor input

build_payload(kw_list, cat, timeframe, geo, gprop) becomes searchTerms, category (a category id from the input list), timeRange (presets such as today 5-y or now 7-d; leave it empty for the past 12 months) or customTimeRange (2024-01-01 2024-12-31), geo and property (images, news, youtube, froogle, the same values as gprop). interest_over_time() is interestOverTime_timelineData, interest_by_region() is interestBy, interestBySubregion or interestByCity (keep includeGeo on; Google picks the level), related_queries() is relatedQueries_top and relatedQueries_rising, and related_topics() is relatedTopics_top and relatedTopics_rising. For the trending searches of a country, send trendingGeos such as ["US"]: one row per trending search.

More than five keywords

Google compares at most five keywords per request, so pytrends code often loops over batches and rescales them by hand. With Compare keywords on one scale on, the actor does the batching with a shared anchor keyword and puts every keyword on one 0-100 scale; scale on each row says whether the values are exact (up to five keywords) or approximate, with the estimated error.

Output fields

Every row has a rowType; filter on it first. Failed inputs come back as free rows with a code and message. The fields this tutorial uses, from the actor’s dataset schema:

FieldWhat it holds
rowTypeKind of row. Filter on this first. 'trend': one keyword (charged). 'trending': one Trending now search (charged). 'error': an input could not be read; see code and message (free).
searchTermThe keyword this row is about.
geoLocation measured: country or region code, empty for worldwide.
timeRangePeriod as Google writes it, for example today 12-m.
averageInterestMean of the timeline values on the 0-100 scale.
interestOverTime_timelineDataTimeline points with time (Unix seconds), formattedTime, value (0-100), hasData and isPartial.
relatedQueries_risingRising related queries; formattedValue is such as +250% or Breakout.
scaleHow values are scaled: method (single, compare, anchor), keywords, anchor and factorErrorPct.
codeError rows: machine-readable error code.

All fields: Google Trends Scraper guide.

Cost per run

Pay per event: you pay for the rows delivered, at the actor’s live Apify price on 2026-11-02. Error, coverage and summary rows are free.

WhatRowsPrice per row (Free plan)Subtotal
Result row: 2 keywords, one row each2$0.0027$0.0054

Cost of this example on the Apify Free plan: 2 × $0.0027 = $0.0054 ($0.00054 on the Apify Gold plan and higher), plus $0.00005 per GB of memory when the run starts. Higher Apify plans pay less per row. The Store page always has the current price.

Limitations

Links