# Google Trends Scraper - Interest Over Time & Related Queries (`smart-digital/google-trends-scraper`) Actor

Scrape Google Trends by keyword: interest over time with peak, average and momentum already computed, interest by region, and top and rising related queries. Each keyword has its own time budget, so one stalled keyword never costs you the run - and is never charged.

- **URL**: https://apify.com/smart-digital/google-trends-scraper.md
- **Developed by:** [My Smart Digital](https://apify.com/smart-digital) (community)
- **Categories:** SEO tools, Marketing, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.30 / 1,000 keyword analyzeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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, by Region & Related Queries

Give it a list of search terms. Get back one row per term: the **interest-over-time curve**, the
**interest by region**, the **top and rising related queries**, and — already computed — the
**average, the peak and its date, the latest value and the momentum**.

Every keyword is collected on its own, inside its own time budget. A term Google throttles or
stalls on comes back labelled as such while the rest keep going, and **the run still ends and
still writes everything it collected**.

***

### Why this one

The failure mode on this data source is not crashing, it is hanging — and a hung run hands back
nothing at all, including the keywords it had already finished.

This actor makes the **keyword**, not the run, the unit of work:

1. **One keyword never costs you the run.** Each term gets its own deadline (45 s by default,
   configurable). When it expires, that single row is returned with `status: "timed_out"` and the
   real reason in `error`; the run carries on to the next term. 25 keywords over 5 years complete
   in about 47 seconds.
2. **You are never charged for a curve that was not delivered.** The billing event fires once per
   keyword whose interest curve was actually obtained and written to the dataset — never on
   `no_data`, `failed`, `timed_out` or `skipped_run_budget`, and never twice for the same term
   (the list is de-duplicated, case-insensitively, before anything is collected). There is no
   per-run start fee.
3. **The statistics are computed for you, and computed correctly.** Average, peak, peak date,
   latest value and momentum come with the curve. All of them **exclude the final partial
   bucket** — the one Google flags `isPartial`, covering a week or a month that has not finished
   yet. Counting it makes every trend look like it just collapsed.

***

### Input

| Field | Type | Default | What it does |
|---|---|---|---|
| `keywords` | array of strings | — | The search terms, one plain string each. Collected independently. Duplicates removed, case-insensitively. Up to **500 terms per run**. |
| `geo` | string | `""` (worldwide) | Two-letter country code (`US`, `FR`, `GB`) or a sub-region (`US-CA`, `FR-IDF`). |
| `timeRange` | string | `today 12-m` | `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`. Google picks the granularity from the window: hourly under a week, daily up to nine months, weekly beyond, monthly for the full history. |
| `searchType` | string | `web` | Which Google surface the interest is measured on: `web`, `images`, `news`, `youtube`, `shopping`. Only `web` has the full history back to 2004. |
| `category` | integer | `0` | Google Trends category id to narrow the topic (`0` all, `7` finance, `71` food and drink, `174` computers and electronics). |
| `includeRegions` | boolean | `true` | Adds `interestByRegion`. One extra request per keyword, no extra charge. |
| `includeRelated` | boolean | `true` | Adds `topRelatedQueries` and `risingRelatedQueries`. One extra request per keyword, no extra charge. |
| `trendingSearches` | boolean | `false` | Also returns the day's trending searches for the country, with rank and approximate traffic. These rows carry no curve and are **never charged**. |
| `keywordTimeoutSecs` | integer | `45` | How long one keyword may take before it is returned marked as timed out. 10 to 180. Raise it on `all`. |
| `language` | string | `en-US` | Interface language, which drives the region names and date labels Google returns (`en-US`, `fr-FR`, `de-DE`). |

```json
{
  "keywords": ["bitcoin", "seo audit", "coffee"],
  "geo": "US",
  "timeRange": "today 12-m",
  "includeRegions": true,
  "includeRelated": true
}
```

***

### Output

One row per keyword, **always the same shape** whatever happened to that keyword. Real excerpt,
trimmed for length:

```json
{
  "keyword": "bitcoin",
  "geo": "US",
  "timeRange": "today 12-m",
  "searchType": "web",
  "status": "ok",
  "interestOverTime": [
    { "date": "2025-08-31T00:00:00.000Z", "label": "Aug 31 – Sep 6, 2025", "value": 25, "hasData": true, "partial": false }
  ],
  "dataPoints": 54,
  "averageInterest": 35.3,
  "peakInterest": 100,
  "peakDate": "2026-02-01T00:00:00.000Z",
  "latestInterest": 20,
  "momentum": -7.9,
  "interestByRegion": [
    { "geoCode": "US-WY", "geoName": "Wyoming", "value": 100, "hasData": true }
  ],
  "regionsWithData": 51,
  "topRelatedQueries": [
    { "query": "bitcoin price", "value": 100, "formattedValue": "100", "breakout": false }
  ],
  "risingRelatedQueries": [
    { "query": "how to buy bitcoin safely", "value": 13600, "formattedValue": "Breakout", "breakout": true }
  ],
  "warnings": [],
  "error": null,
  "exploreUrl": "https://trends.google.com/trends/explore?q=bitcoin&date=today+12-m&hl=en-US&geo=US",
  "collectedAt": "2026-09-06T07:24:55.889Z",
  "durationMs": 4501
}
```

| Field | Meaning |
|---|---|
| `status` | `ok`, `no_data`, `failed`, `timed_out`, `skipped_run_budget`, or `trending`. See the table below. |
| `interestOverTime` | The curve, point by point. `value` is Google's 0-100 relative index, `partial` marks the unfinished last bucket, `hasData` is false where Google reports nothing. |
| `dataPoints` | Number of points in the curve. |
| `averageInterest` | Mean of the curve, partial bucket excluded. |
| `peakInterest` / `peakDate` | Highest value and when it happened. |
| `latestInterest` | Last complete value. |
| `momentum` | Second half of the window minus the first half, in index points. Positive means rising. |
| `interestByRegion` | One entry per region, `value` on the same 0-100 scale. Read the caveat below. |
| `regionsWithData` | How many regions actually returned something. |
| `topRelatedQueries` | Most searched associated queries, `value` normalised to 100. |
| `risingRelatedQueries` | Fastest growing associated queries. `breakout: true` means growth Google does not put a number on. |
| `trendingRank`, `trendingTraffic`, `trendingPublishedAt` | Filled only on trending-search rows. |
| `warnings` | Non-fatal problems on an otherwise good row, e.g. the region panel did not answer. |
| `error` | Why this keyword has no curve, in plain words. `null` when it has one. |
| `exploreUrl` | The Google Trends page that shows the same query, so any row can be checked by hand. |
| `durationMs` | How long this keyword took. |

#### What `status` tells you

| Value | Meaning | Charged |
|---|---|---|
| `ok` | Curve obtained and delivered. | Yes |
| `no_data` | Google has no curve for this term: too little search volume. Curve, regions and related queries are empty rather than invented. | No |
| `failed` | The source returned an error for this term. `error` carries it verbatim. | No |
| `timed_out` | The term exceeded the time it was granted. `error` quotes the allowance actually given, not the one configured. | No |
| `skipped_run_budget` | The run hit its own time limit before reaching this term. It is still returned, saying so. | No |
| `trending` | A trending-search row, no curve by design. | No |

***

### Three things about this data that are easy to get wrong

**Interest is an index, not a volume.** Google Trends returns a relative 0-100 scale where 100 is
the peak of that particular query over that particular window. It never returns a number of
searches. Any tool telling you otherwise is guessing.

**Interest by region is a share of local searches, not a ranking of places.** Wyoming comes back
at 100 for `bitcoin`, `seo audit` and `coffee` alike — a sparsely populated region hits the
ceiling mechanically, because the metric is the weight of the term *within* that region's
searches. Use it to compare a term against itself across places, not to find where the volume is.

**Related *topics* are not returned, on purpose.** Google's fourth panel only answers for
entity identifiers, and comes back empty for plain search terms — measured empty 4 times out of 4,
against 25 top and 25 rising related *queries* on the very same requests. An always-empty column
is a lie, so it is not shipped.

***

### Limits, stated plainly

- **No absolute search volume.** The source does not have it. See above.
- **No related topics.** See above.
- **No multi-term comparison on one chart.** Each keyword is measured on its own scale, which is
  what makes them independent and individually chargeable. Two rows are not comparable point to
  point.
- **500 keywords per run maximum.** Past that the extra terms are dropped and the run log says so.
- **Rate limiting happens, and it is absorbed rather than hidden.** Google occasionally throttles
  the opening burst of a run; those keywords come back `failed` with the real reason, unbilled,
  and the rest of the run completes. Retrying them in a second run normally works.
- **Granularity is Google's, not yours.** Ask for 5 years and you get weekly points; ask for a day
  and you get hourly ones. There is no finer setting.

***

### Cost

One event per keyword whose interest curve was obtained and delivered. Nothing on a keyword that
returned no data, failed, timed out or was never reached; nothing on trending-search rows; no
per-run start fee. The current rate is on this actor's pricing section.

Runs are light: 25 keywords over 5 years finish in about 47 seconds and peak at well under
100 MB of memory, so 256 MB is enough.

***

### Typical uses

- **Seasonality before you commit a budget.** Pull 12 months on your product terms, sort by
  `peakDate`, and buy media before the peak rather than during it.
- **Spotting a term going up.** `momentum` over `today 12-m` on a list of category terms, filtered
  to positives, is a short list of what is gaining ground.
- **Local targeting.** `interestByRegion` on `US` or `FR` tells you where a term weighs most in
  local search, which is where landing pages and local campaigns earn their keep.
- **Content ideas that already have demand.** `risingRelatedQueries` with `breakout: true` is a
  list of questions people started asking recently.

# Actor input Schema

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

Search terms to analyze, one plain string per line. Each one is collected independently, so a term Google throttles never affects the others. Duplicates are removed, and up to 500 terms are collected per run.

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

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

## `timeRange` (type: `string`):

The window the interest curve covers. Google changes the granularity with the window: hourly under a week, daily up to nine months, weekly beyond, monthly for the full history.

## `searchType` (type: `string`):

Which Google surface the interest is measured on. Web search is the default and the only one with full history.

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

Optional Google Trends category ID to narrow the topic (0 is all categories, 7 finance, 71 food and drink, 174 computers and electronics). Leave at 0 unless you know the ID.

## `includeRegions` (type: `boolean`):

Add the breakdown of interest by region for each keyword. Costs one extra request per keyword and nothing extra to you.

## `includeRelated` (type: `boolean`):

Add the top and rising related queries for each keyword, with breakout terms flagged as such. Costs one extra request per keyword and nothing extra to you.

## `trendingSearches` (type: `boolean`):

Also return the trending searches of the day for the chosen country, with their rank and approximate traffic. These rows carry no interest curve and are never charged.

## `keywordTimeoutSecs` (type: `integer`):

How long a single keyword may take before it is returned marked as timed out and the run moves on. Raise it on slow windows such as 2004 to now.

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

Interface language code, which drives the region names and date labels Google returns (en-US, fr-FR, de-DE).

## Actor input object example

```json
{
  "keywords": [
    "bitcoin",
    "seo audit",
    "coffee"
  ],
  "geo": "",
  "timeRange": "today 12-m",
  "searchType": "web",
  "category": 0,
  "includeRegions": true,
  "includeRelated": true,
  "trendingSearches": false,
  "keywordTimeoutSecs": 45,
  "language": "en-US"
}
```

# Actor output Schema

## `keywords` (type: `string`):

Compact table: keyword, status, average and peak interest, momentum.

## `allFields` (type: `string`):

Every field, including the full interest curve point by point, interest by region, and related queries and topics.

# 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": [
        "bitcoin",
        "seo audit",
        "coffee"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("smart-digital/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": [
        "bitcoin",
        "seo audit",
        "coffee",
    ] }

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

```

## MCP server setup

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