# Google Trends Scraper — Interest, Regions, Queries & Topics (`diopside/google-trends`) Actor

Google Trends interest over time, interest by region and top/rising related queries and topics for any keyword, from the site's own JSON API — no browser. Warms the session cookie and rotates IPs on HTTP 429, so runs finish instead of timing out.

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

## Pricing

from $2.00 / 1,000 search terms

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Trends Scraper — interest over time, by region, related queries & topics

**Drop-in compatible with `apify/google-trends-scraper` — same input fields, same billing unit, lower
price, and it finishes.** Search-interest data straight from Google Trends' own JSON API: how interest
in a keyword moved over time, where it is strongest, and which queries and topics are rising alongside
it. No API key, no browser, no Google account.

One run gives you every panel you see on `trends.google.com/trends/explore`:

| Panel | `record_type` | Typical size (1 term, 12 months, US) |
|---|---|---|
| Interest over time | `interest_over_time` | 53 weekly points |
| Interest by region | `interest_by_region` | 51 US states |
| Related queries (top & rising) | `related_query` | 25 top + 25 rising |
| Related topics (top & rising) | `related_topic` | 25 top + ~10 rising |

Related topics come back as entities, not strings — `topic_mid` (`/m/05p0rrx`), `topic_type`
(`Cryptocurrency`) and a title — so they survive spelling variants and translation, which is the whole
reason to prefer them over related queries.

### Why this one

Google Trends rate-limits hard, and that is where the other scrapers in this category lose runs.
Two things trigger an HTTP 429, and this actor handles both:

- **A session with no Google cookie is 429'd on its very first call.** Touching a Trends page once
  sets the `NID` cookie Google requires; every call on that session then succeeds. This actor warms
  each HTTP session before it asks for data, and re-warms after every IP change.
- **One IP has a request budget.** Steady traffic burns it in about a minute, and Google then answers
  429 to everything from that IP until it cools down. Instead of retrying the dead IP — or hanging
  until the platform timeout — this actor climbs a **proxy ladder**: fresh IP in your proxy tier,
  then again, then Apify RESIDENTIAL. Every new IP gets its own sticky proxy session and its own
  warmup, so one IP keeps one cookie jar. The run reports which tier it finished on.

It also runs without a browser. The leading actor in this category drives a headless Chrome at
4096 MB; this one talks to the same JSON API Google's own page calls, in 1024 MB, so a one-term run
finishes in ~12s of platform time instead of ~36s — which is most of why the per-term price is lower.

**Related topics without a browser.** Google classifies the session that asks for a widget token and
writes the verdict into the token itself (`userConfig.userType`). A token minted by `/trends/api/explore`
is stamped `USER_TYPE_SCRAPER`, and Google answers the related-*topics* route for such a token with an
empty list — while answering related *queries* in full from the very same token, seconds apart. The
browser-based actors in this category get the panel because they render Google's *embed* widget, whose
token is stamped `USER_TYPE_EMBED`. This actor fetches that embed token directly — one extra HTML
request, no Chrome, no 4 GB — so you get the panel at the API path's speed and price. If a related
panel ever comes back empty from the explore token, the embed token is tried automatically, so the
same fallback covers related queries if Google ever withholds those too.

Everything else is built the same way. Widget tokens, comparison layouts and request bodies are
**discovered at run time** — the actor replays Google's own `explore` response rather than
constructing requests from hardcoded templates, so a layout change upstream does not break it. A
panel Google has no data for answers `204 No Content`; that is treated as an answer (zero records
plus a note in the status message), not as an error worth retrying on four more IPs. Missing values
are `null`.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `searchTerms` | string\[] | — | **Required.** One set of results per term. |
| `timeRange` | string | `today 12-m` | Google's own codes: `""` (default), `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`. |
| `customTimeRange` | string | `""` | `2024-01-01 2024-12-31`. Overrides `timeRange`. |
| `geo` | string | `""` (worldwide) | `US`, `GB`, `US-NY`, `AU-NSW`, `US-NY-501`. |
| `searchType` | string | `Web Search` | `Web Search`, `Image Search`, `News Search`, `Google Shopping`, `YouTube Search`. |
| `category` | string | `""` | Google Trends category id as a string, same list as the other actors: `"7"` Finance, `"16"` News, `"71"` Food, `"174"` Sports… `""` = all. |
| `categoryId` | integer | `0` | Any category id, including the ~1400 sub-categories the list above does not show (`1138` Cryptocurrency). Overrides `category`. |
| `outputs` | string\[] | all four | `interestOverTime`, `interestByRegion`, `relatedQueries`, `relatedTopics`. Billing is per search term, so extra panels are free. |
| `geoResolution` | string | auto | `COUNTRY`, `REGION`, `CITY`, `DMA` for the region panel. |
| `isMultiple` | boolean | `false` | `true` scores terms against each other in groups of five, like the compare view. |
| `language` | string | `en-US` | Affects region names and formatted labels. |
| `maxItems` | integer | `0` | Hard cap on records, and therefore on cost. |
| `proxyConfiguration` | object | Apify proxy | The tier tried first. |
| `residentialFallback` | boolean | `true` | Allow escalation to Apify RESIDENTIAL when a tier keeps getting 429. |

#### Switching from `apify/google-trends-scraper`

Change the actor id. `searchTerms`, `isMultiple`, `timeRange`, `customTimeRange`, `geo`, `category`
and `maxItems` keep their names, types and allowed values — including `timeRange: ""` and the string
category ids — so an input written for that actor runs here unchanged. Fields it has that we do not
(`maxConcurrency`, `maxRequestRetries`, `pageLoadTimeoutSecs`, `skipDebugScreen`) are browser-tuning
knobs; this actor talks to Google's JSON API directly and does not need them.

The **output is deliberately different**: that actor returns one dataset item per search term with
every panel nested inside (`interestOverTime_timelineData`, `interestBySubregion`, `relatedTopics_top`
…). This one returns flat rows — one per data point, tagged with `record_type` — which is what you
want for a warehouse load, a CSV, or a filter on one panel. The billing unit is the same either way:
one charge per search term.

### Output

One flat record per data point. `record_type` tells you which panel it came from; fields that do not
apply to a record type are `null`. The dataset ships with four views (all records, interest over
time, interest by region, related) so the console and CSV exports stay readable.

```json
{
  "record_type": "interest_over_time",
  "search_term": "bitcoin",
  "geo": "US",
  "time_range": "today 12-m",
  "search_type": "Web Search",
  "category": 0,
  "language": "en-US",
  "is_comparison": false,
  "date": "2026-03-15",
  "time_utc": "2026-03-15T00:00:00Z",
  "timestamp": 1773532800,
  "formatted_time": "Mar 15 - 21, 2026",
  "value": 74,
  "formatted_value": "74",
  "is_partial": false,
  "scraped_at": "2026-09-18T12:00:00Z"
}
```

```json
{
  "record_type": "related_query",
  "search_term": "bitcoin",
  "rank_list": "rising",
  "rank": 1,
  "query": "how to buy bitcoin safely",
  "value": 17000,
  "formatted_value": "Breakout",
  "link": "https://trends.google.com/trends/explore?q=how+to+buy+bitcoin+safely&date=today+12-m&geo=US"
}
```

```json
{
  "record_type": "related_topic",
  "search_term": "bitcoin",
  "rank_list": "top",
  "rank": 1,
  "query": "Bitcoin",
  "topic_mid": "/m/05p0rrx",
  "topic_type": "Cryptocurrency",
  "value": 100,
  "formatted_value": "100",
  "link": "https://trends.google.com/trends/explore?q=/m/05p0rrx&date=today+12-m&geo=US"
}
```

Two details worth knowing, because they change how the numbers should be read:

- `value` is Google's 0-100 index, relative to the peak **within that query**. Two separate runs are
  not comparable; use `isMultiple` when you need terms on one scale.
- `is_partial` is `true` on the final time bucket, which is still filling up. Averaging it in without
  noticing shows a fake drop at the end of every chart.
- On rising related queries and topics `value` is a percentage increase, and Google labels anything
  enormous as `Breakout` in `formatted_value`.
- Related topics carry `topic_mid` and `topic_type`; related queries carry neither and leave them
  `null`. Both put a human-readable label in `query`, so one column works for both.

### Sizes and cost

**You are billed per search term, not per record.** One term that returns 53 weekly points, 51 states
and 50 related queries and 35 related topics is one charge — the same charge as a term you asked a single panel for. Ask for
every panel and the longest range; the price does not move. A term Google has no data for is free.

| Query | Records | Billed |
|---|---|---|
| 1 term, 12 months, US, default panels | ~190 | 1 term |
| 1 term, 5 years, worldwide, default panels | ~600 | 1 term |
| 100 terms, 12 months, US, default panels | ~19,000 | 100 terms |
| 3 terms compared, 90 days, GB | 291 | 3 terms |

`maxItems` caps the size of the dataset, not the price — a term is billed the first time it returns a
record. To cap spend, cap `searchTerms` or set a charge limit on the run.

Other actors in this category quote the same unit — a search term — but hand it back as one dataset
item with every panel nested inside. We charge the same unit and give you flat rows instead, so you
can filter, join and load them without unpacking anything.

### Use cases

- Track demand for a product or brand week by week and feed it into a forecast.
- Find the regions where a campaign should run, at state, metro or city level.
- Mine rising related queries and topics for SEO and content briefs before the term peaks.
- Follow topic entities (`topic_mid`) instead of strings, so one concept stays one row
  across spellings and languages.
- Compare up to five competitors on one scale with `isMultiple`.
- Watch a category (`category` + `News Search`) for emerging stories.
- Score demand for a keyword on YouTube or Google Shopping rather than web search.

### Notes

- Public Google Trends data only. No personal data, no login, no Google account.
- Google's numbers are a relative index, not absolute search volume — that is Trends, not the actor.
- A run on datacenter proxies is normal; residential is only used when a tier keeps getting 429, and
  is charged by the GB on your account. Set `residentialFallback: false` to forbid it.

# Actor input Schema

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

Keywords or topics to look up on Google Trends, e.g. bitcoin, electric car. One set of results per term (or one comparison per group of five, when 'Compare terms' is on).

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

How far back to look. Uses Google's own range codes, so the numbers match what trends.google.com shows — and the same codes other Google Trends actors accept, including the empty default.

## `customTimeRange` (type: `string`):

Overrides 'Time range' when set. Two dates separated by a space: 2024-01-01 2024-12-31.

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

ISO country code (US, GB, DE), a subregion (US-NY, AU-NSW) or a metro (US-NY-501). Leave empty for worldwide.

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

Which Google surface the interest is measured on.

## `category` (type: `string`):

Google Trends category to narrow the query to. Same values other Google Trends actors use, so an existing input carries over unchanged. Use 'Category ID' below for a category that is not in this list.

## `categoryId` (type: `integer`):

Any Google Trends category ID, including the ~1400 sub-categories the list above does not show (e.g. 1138 Cryptocurrency). Overrides 'Category' when not 0.

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

Which Google Trends panels to export. Each produces its own record type in the dataset. You are billed per search term, not per record, so asking for every panel costs the same as asking for one.

## `geoResolution` (type: `string`):

Granularity of 'Interest by region'. Auto follows what Google shows for the location (countries worldwide, states for a country). CITY and DMA only return data for a single-country location.

## `isMultiple` (type: `boolean`):

Off (default): every term is scored 0-100 on its own scale. On: terms are compared against each other in groups of five, exactly like the compare view on trends.google.com.

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

Google interface language, e.g. en-US, de, fr. Affects region names and formatted labels only.

## `maxItems` (type: `integer`):

Hard cap on dataset records. 0 = no cap. Cost is per search term, so this caps dataset size rather than price — a term is billed once, the first time it returns a record.

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

Google Trends rate-limits by IP. This is the proxy tried first; when a tier runs out of budget the actor rotates the IP by itself and finally escalates to Apify RESIDENTIAL — see 'Residential fallback'.

## `residentialFallback` (type: `boolean`):

When the proxy above keeps getting HTTP 429 from Google, automatically retry on Apify RESIDENTIAL proxies (country US). Residential traffic is billed by the GB on your account; turn this off to keep a run strictly on the proxy you chose.

## Actor input object example

```json
{
  "searchTerms": [
    "bitcoin",
    "ethereum"
  ],
  "timeRange": "today 12-m",
  "customTimeRange": "",
  "geo": "US",
  "searchType": "Web Search",
  "category": "",
  "categoryId": 0,
  "outputs": [
    "interestOverTime",
    "interestByRegion",
    "relatedQueries",
    "relatedTopics"
  ],
  "geoResolution": "",
  "isMultiple": false,
  "language": "en-US",
  "maxItems": 0,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "residentialFallback": true
}
```

# Actor output Schema

## `records` (type: `string`):

All records. Append ?format=csv for CSV.

## `datasetUrl` (type: `string`):

The default dataset.

# 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"
    ],
    "timeRange": "today 12-m",
    "geo": "US",
    "outputs": [
        "interestOverTime",
        "interestByRegion",
        "relatedQueries",
        "relatedTopics"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("diopside/google-trends").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",
    ],
    "timeRange": "today 12-m",
    "geo": "US",
    "outputs": [
        "interestOverTime",
        "interestByRegion",
        "relatedQueries",
        "relatedTopics",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("diopside/google-trends").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"
  ],
  "timeRange": "today 12-m",
  "geo": "US",
  "outputs": [
    "interestOverTime",
    "interestByRegion",
    "relatedQueries",
    "relatedTopics"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call diopside/google-trends --silent --output-dataset

```

## MCP server setup

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

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/jlTfMrauchAcbPeBM/builds/pTvwQV4QfFKiHEL7i/openapi.json
