# Google Trends Scraper (`sellerkit/google-trends-scraper`) Actor

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.

- **URL**: https://apify.com/sellerkit/google-trends-scraper.md
- **Developed by:** [SellerKit](https://apify.com/sellerkit) (community)
- **Categories:** SEO tools, AI, News
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.80 / 1,000 result rows

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/actors/running/actors-in-store.md#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, 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

| `dataType` | Fields | Rows per term |
|---|---|---|
| `interestOverTime` | `date`, `timestamp`, `value` (0-100), `isPartial` | 53 for 12 months, 93 for 90 days |
| `interestByRegion` | `region`, `regionCode`, `value` (0-100) | 51 for the US, up to ~250 worldwide |
| `relatedQueries` | `query`, `value`, `formattedValue`, `isBreakout`, `link` | up 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.

#### Related topics is not offered

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:

```json
{
  "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:

| `reason` | Meaning | Worth retrying |
|---|---|---|
| `empty-from-google` | Google answered, with nothing in it. Usually a term with no measurable interest in that region or timeframe. | No |
| `not-offered-by-google` | Google did not offer this widget for this term at all. | No |
| `bad-request` | Google rejected the term, geo or timeframe as invalid. | No, fix the input |
| `deadline` | The `secondsPerTerm` budget ran out. | Yes, with a longer budget |
| `exhausted` | Rate 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

```json
{
  "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.

# Actor input Schema

## `searchTerms` (type: `array`):

Terms to look up. Each one is fetched separately, so a term Google refuses to answer does not affect the rest.

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

Two-letter country code such as US, GB, KR, or a sub-region such as US-CA. Leave empty for worldwide.

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

How far back to look. Longer ranges return coarser buckets: 12 months gives weekly points, 7 days gives hourly.

## `outputs` (type: `array`):

Related topics is not offered because Google currently returns an empty list for it. If that changes it will be added here.

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

Google Trends category id. 0 is all categories. For example 7 is Finance, 71 is Food & Drink, 5 is Computers & Electronics.

## `language` (type: `string`):

Language for region names and formatted values, for example en-US, ko, de.

## `secondsPerTerm` (type: `integer`):

A term that cannot be fetched within this budget is recorded as skipped and the run moves on. This is what stops a run from hanging until the platform kills it.

## `maxRetriesPerRequest` (type: `integer`):

Google answers an un-warmed request with 429. Each retry backs off and, from the second attempt, asks the proxy for a new address.

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

Strongly recommended. Google rate limits per address, so without a proxy the retries cannot change anything.

## Actor input object example

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

# Actor output Schema

## `results` (type: `string`):

Every collected row. One row per data point, with dataType saying which output it belongs to.

## `resultsCsv` (type: `string`):

The same rows as a spreadsheet.

## `runSummary` (type: `string`):

Which terms were collected, which came back partial, and which were skipped with the reason. Watch this rather than the row count.

# 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 = {
    "searchTerms": [
        "bitcoin",
        "ethereum"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("sellerkit/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 = { "searchTerms": [
        "bitcoin",
        "ethereum",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("sellerkit/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 '{
  "searchTerms": [
    "bitcoin",
    "ethereum"
  ]
}' |
apify call sellerkit/google-trends-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,sellerkit/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/heLy44Gz5W9lExb0r/builds/ZFaBL1nhDzMWDgx88/openapi.json
