# Google Trends Scraper — Interest Over Time & By Region (`ponderable_hydrometer/google-trends-scraper`) Actor

Google Trends data as clean JSON. Compare up to 5 search terms, get interest over time or interest by region, any country and timeframe. Survives Google's rate limiting by rotating IPs automatically instead of failing the run.

- **URL**: https://apify.com/ponderable\_hydrometer/google-trends-scraper.md
- **Developed by:** [Ponderable Hydrometer](https://apify.com/ponderable_hydrometer) (community)
- **Categories:** SEO tools, Marketing, Business
- **Stats:** 2 total users, 1 monthly users, 90.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Google Trends Scraper — Interest Over Time & By Region

Google Trends data as clean JSON. Compare up to five search terms, for any country and
any timeframe, and get back either a time series or a regional breakdown — one flat row
per data point, ready for a spreadsheet or a dataframe.

### Why this one

Google publishes no official Trends API. The endpoint behind trends.google.com works, but
it rate-limits aggressively — it will happily hand you a valid session token and then
answer `429` to the request that actually carries the data. That is why Trends scrapers
have a reputation for failing halfway through.

This actor treats a `429` as *"this IP is spent"*, not *"the request was wrong"*. Each
retry runs on a **new residential IP with a fresh session**, so a rate-limited attempt
costs you a few seconds instead of a failed run.

That isn't a theoretical feature. Here's a real log line from the run that produced the
regional example below:

```
WARN  Trends attempt 1/4 failed: comparedgeo HTTP 429 — rotating IP
WARN  Trends attempt 2/4 failed: comparedgeo HTTP 429 — rotating IP
INFO  Done. rows=102
```

Two IPs were rate-limited. You still got your 102 rows.

### Input

| Field | Type | Notes |
|---|---|---|
| `keywords` | array | Up to 5 terms to compare. Required. |
| `geo` | string | `"US"`, `"DE"`, `"RO"`, or a sub-region like `"US-CA"`. Empty = worldwide. |
| `timeframe` | string | `now 7-d`, `today 1-m`, `today 12-m`, `today 5-y`, `all`, or `"2024-01-01 2024-12-31"`. |
| `dataType` | string | `timeseries` (interest over time) or `geo` (interest by region). |
| `category` | integer | Google Trends category id. `0` = all categories. |
| `proxyConfiguration` | object | Residential by default. Changing this will likely produce `429`s. |

### Output

**`dataType: "timeseries"`** — one row per date per keyword:

```json
{
  "keyword": "chatgpt",
  "date": "Jul 26, 2026",
  "timestamp": "2026-07-26T00:00:00.000Z",
  "value": 65,
  "isPartial": false,
  "geo": "US",
  "timeframe": "today 12-m",
  "scrapedAt": "2026-08-04T12:35:37.000Z"
}
```

**`dataType: "geo"`** — one row per region per keyword:

```json
{
  "keyword": "skiing",
  "geoName": "Vermont",
  "geoCode": "US-VT",
  "value": 93,
  "hasData": true,
  "geo": "US",
  "timeframe": "today 12-m",
  "scrapedAt": "2026-08-04T12:37:17.000Z"
}
```

`isPartial: true` marks the final bucket of a series, where the period hasn't finished yet
— useful to exclude before charting, or it will look like a sudden drop.

### The one thing to understand about the numbers

Trends values are **relative, not absolute**. `100` is the peak of *this particular
comparison*, and everything else is scaled against it. There is no search-volume figure
hiding underneath.

The practical consequence: adding or removing a keyword rescales every other series. Two
runs with different keyword sets are not comparable to each other. If you need a stable
baseline across many runs, keep one fixed reference term in every query.

### Typical uses

- Track a brand against its competitors over time
- Find which regions actually want a product before spending on ads there
- Check whether a term is genuinely breaking out or just seasonal
- Seasonality planning: pull `today 5-y` and look at the repeating shape

### Limits

- **Query shape changes how hard Google throttles.** Measured: a 2-term query scoped to a
  country succeeded repeatedly in the same minutes that a 3-term *worldwide* query was
  refused on seven consecutive residential IPs. If a run fails, the fix is almost always to
  narrow it — set `geo` to a country, or compare fewer terms — not to retry the same thing.
  The actor says so in the error rather than failing silently.
- Google compares at most **5 terms** per query — the actor rejects more instead of
  silently dropping them.
- Very low-volume terms come back as an empty series. That's Google having no data, not a
  scrape failure; the run logs a warning and finishes clean.
- Requests go through residential proxy by default because datacenter ranges are what
  Google blocks hardest.

# Actor input Schema

## `keywords` (type: `array`):

Up to 5 terms to compare, e.g. \["chatgpt", "claude"]. Values are relative interest (0-100), scaled against the peak of the whole comparison — so adding or removing a term rescales the others.

## `geo` (type: `string`):

Two-letter country code ("US", "DE", "RO") or a sub-region like "US-CA". Leave empty for worldwide.

## `timeframe` (type: `string`):

Google Trends syntax: "now 1-H", "now 7-d", "today 1-m", "today 12-m", "today 5-y", "all", or an explicit range like "2024-01-01 2024-12-31".

## `dataType` (type: `string`):

"timeseries" = interest over time (one row per date per term). "geo" = interest by region (one row per region per term).

## `category` (type: `integer`):

Google Trends category id to narrow the query. 0 = all categories. See Google's category list for ids.

## `proxyConfiguration` (type: `object`):

Google rate-limits datacenter IPs hard, so residential proxy is the default and is strongly recommended. Changing this is likely to produce 429 errors.

## Actor input object example

```json
{
  "keywords": [
    "chatgpt",
    "claude"
  ],
  "geo": "US",
  "timeframe": "today 12-m",
  "dataType": "timeseries",
  "category": 0,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "keywords": [
        "chatgpt",
        "claude"
    ],
    "geo": "US",
    "timeframe": "today 12-m",
    "dataType": "timeseries",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("ponderable_hydrometer/google-trends-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "keywords": [
        "chatgpt",
        "claude",
    ],
    "geo": "US",
    "timeframe": "today 12-m",
    "dataType": "timeseries",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("ponderable_hydrometer/google-trends-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "keywords": [
    "chatgpt",
    "claude"
  ],
  "geo": "US",
  "timeframe": "today 12-m",
  "dataType": "timeseries",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call ponderable_hydrometer/google-trends-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ponderable_hydrometer/google-trends-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/fIVojmKdKAqsIkdmB/builds/CxFLTvbtGtIuc7wQS/openapi.json
