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

Reliable Google Trends data: interest over time, interest by region, related queries/topics and daily trending searches, with automatic retries, session/proxy rotation and honest error reporting instead of silent failures.

- **URL**: https://apify.com/gravelly\_caladium/google-trends-scraper.md
- **Developed by:** [Relay Data Tools](https://apify.com/gravelly_caladium) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

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

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

Google Trends data - interest over time, interest by region, related queries/topics, and
today's trending searches - pulled directly from the same internal endpoints
trends.google.com's own web app uses, with the reliability engineering that endpoint
actually needs: automatic retries with backoff, session/identity rotation, Apify Proxy
support, and **honest error reporting instead of silent failures or a crashed run.**

### Who it's for

- **SEO and content teams** researching rising queries and regional demand before writing or
  prioritizing content, and content planners mapping out a publishing calendar around a
  topic's seasonality (e.g. "when does interest in `tax software` start climbing each year").
- **Market/competitive researchers** comparing interest in brands, products, or topics over
  time and across countries.
- **Anyone automating a "what's trending" feed** for a newsletter, dashboard, or social
  media queue.
- Anyone who has been burned by a Trends actor that silently returns nothing, or fails,
  on a chunk of their runs - see "Why this Actor exists" below.

### Why this Actor exists

Google Trends has no public API. Every Trends actor on the market, including this one,
works by replaying the same undocumented `trends.google.com/trends/api/*` calls the
website itself makes, and Google throttles that traffic aggressively and inconsistently.
The result, in practice, is that most Trends actors fail on a meaningful fraction of runs.
This Actor treats that as the actual product problem to solve: every request goes through
retry + exponential backoff + a full identity rotation (new User-Agent, new cookies, and -
if you attach Apify Proxy - a new IP) rather than failing the run on the first 429, and
every piece of data that couldn't be fetched is reported as an explicit error item instead
of being dropped silently.

### Input

```json
{
  "searchTerms": ["apify", "web scraping"],
  "geo": "",
  "timeframe": "today 12-m",
  "category": 0,
  "property": "",
  "includeInterestOverTime": true,
  "includeInterestByRegion": true,
  "regionResolution": "COUNTRY",
  "includeRelatedQueries": true,
  "includeRelatedTopics": false,
  "trendingNow": false,
  "trendingNowGeo": "US",
  "proxyConfiguration": { "useApifyProxy": true },
  "maxRetries": 5,
  "requestDelayMs": 1000
}
```

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `searchTerms` | array of strings | `[]` | Up to 5 compared directly per request (Google's own limit). More than 5 are auto-batched - see "Comparing more than 5 terms" below. Can be empty if `trendingNow` is enabled. |
| `anchorTerm` | string | first term | Only relevant with >5 `searchTerms`: the term repeated in every batch to keep values comparable across batches. |
| `geo` | string | `""` (worldwide) | Two-letter country code (`"US"`), a region code (`"US-CA"`), or empty. |
| `timeframe` | string | `"today 12-m"` | One of the presets (`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`) or `"custom"`. |
| `customTimeframeStart` / `customTimeframeEnd` | string (`YYYY-MM-DD`) | - | Required when `timeframe` is `"custom"`. |
| `category` | integer | `0` | Google Trends category ID (`0` = all categories). |
| `property` | string | `""` (web) | `""`, `"news"`, `"images"`, `"youtube"`, or `"froogle"` (Google Shopping). |
| `includeInterestOverTime` | boolean | `true` | Emit `interest_over_time` rows. |
| `includeInterestByRegion` | boolean | `true` | Emit `interest_by_region` rows. |
| `regionResolution` | string | `"COUNTRY"` | `COUNTRY`, `REGION`, `CITY`, or `DMA` (US media markets only). |
| `includeRelatedQueries` | boolean | `true` | Emit `related_query` rows. |
| `includeRelatedTopics` | boolean | `false` | Emit `related_topic` rows. Off by default - see Limitations. |
| `trendingNow` | boolean | `false` | Emit today's trending searches for `trendingNowGeo`, independent of `searchTerms`. |
| `trendingNowGeo` | string | `"US"` | Country code for the trending-now feed. |
| `proxyConfiguration` | object | Apify Proxy off | Strongly recommended for anything beyond a handful of terms. |
| `maxRetries` | integer | `5` | Retries per request before that piece of data is recorded as an error. |
| `requestDelayMs` | integer | `1000` | Politeness delay (+/-25% jitter) before each request. |

#### Comparing more than 5 terms

Google Trends compares at most 5 terms per request, and always re-normalizes so the single
highest point across the batch equals 100. To support more terms, this Actor splits them
into batches of <=5 that all repeat one **anchor term**, then rescales every other batch
onto the first batch's scale using the ratio between the anchor's values in each batch
(median ratio over overlapping points, to smooth out rounding). Rescaled rows carry a
`rescaleFactor` field so you can see exactly what was applied and audit it. This is
documented here rather than presented as an exact re-derivation of Google's own
normalization - treat cross-batch comparisons as a close approximation, not an exact score.

### Output

One dataset row per data point, tagged with a `type` field so different kinds of rows share
one dataset:

| `type` | One row per | Key fields |
| --- | --- | --- |
| `interest_over_time` | (term, date) | `term`, `date`, `value` (0-100 or `null`), `isPartial` |
| `interest_by_region` | (term, location) | `term`, `geoCode`, `geoName`, `value`, `resolution` |
| `related_query` | (term, rank) | `term`, `rankType` (`top`/`rising`), `rank`, `query`, `value`, `formattedValue`, `isBreakout` |
| `related_topic` | (term, rank) | same as `related_query` plus `topicTitle`, `topicType`, `topicMid` |
| `trending_now` | trending item | `rank`, `title`, `approxTraffic`, `pubDate`, `newsItems` |
| `batch_summary` | failed request | `terms`/`term`, `output`, `error` |
| `validation_error` | bad input field | `message` |
| `run_summary` | once per run | counts, resolved `geo`/`timeframe`, `requestsMade` |

`value: null` means Google reported no data for that specific point/term - never confused
with a real `0`. A `noData: true` row (with a `note`) is emitted instead of nothing when an
enabled output returned zero results for a term, so an empty result is always visible and
explained rather than looking like the Actor just skipped that term.

A ready-to-use **Overview** table view is available in the dataset UI/API.

### Limitations

- **Google's `RELATED_TOPICS` widget returned no data in testing, even for popular terms.**
  During this Actor's development (2026-09-28), `RELATED_QUERIES` reliably returned rich
  data while `RELATED_TOPICS` came back as an empty list for every term tested, including a
  generic, high-volume term ("python", worldwide) that has obvious related topics on the
  live trends.google.com site. This looks like a gap in the current redesign of Google's
  Trends API rather than anything this Actor is doing wrong. `includeRelatedTopics` is
  therefore off by default; when enabled, a term that gets nothing back produces an explicit
  `related_topic` row with `noData: true` and a `note`, not silence. If Google fixes this
  server-side, the same code path will pick up real data automatically.
- **Related topics always costs one extra request pair per term.** Google's API only
  returns the `RELATED_TOPICS` widget for a single-term explore call, never for a >1-term
  comparison batch. So whenever `includeRelatedTopics` is on, this Actor issues one
  dedicated single-term `explore` + widget call per term, on top of whatever batch that term
  was already part of for the other outputs.
- **Cross-batch rescaling (>5 terms) is an approximation**, not an exact re-derivation of
  Google's normalization - see "Comparing more than 5 terms" above.
- **The older `dailytrends`/realtime-trends JSON endpoints are gone.** They returned `404`
  in testing; `trendingNow` uses the `trending/rss` feed, which was live and working.
- **No official rate limit is documented.** This Actor's retry/backoff/rotation defaults are
  a reasonable starting point, not a guarantee - very large or very fast runs should attach
  Apify Proxy (`proxyConfiguration`) and consider raising `requestDelayMs`.
- **A snapshot, not a scan.** Trends data (especially `related_query`/`related_topic`
  rankings and `trending_now`) changes hour to hour; treat results as "as of this run."
- **0-100 is relative, not absolute.** Every Trends value is normalized to the peak within
  the requested batch/timeframe/geo - it is a share-of-search-interest metric, never a raw
  search-volume count.

### FAQ

**Why did I get a `batch_summary` item instead of my data?**
Something about that specific request failed after all retries (network error, Google
returned an unexpected shape, or the relevant widget just wasn't in the explore response).
The `error` field says what happened and for which term/output; everything else in the run
still completed normally.

**Why is `includeRelatedTopics` off by default?**
See Limitations - Google's own related-topics widget was unreliable in testing. Turn it on
if your use case can tolerate `noData` rows; `related_query` is unaffected and on by
default.

**Can I run this without a proxy?**
Yes, for light usage (a handful of terms, default delay). Google Trends' throttling is
IP-based, so any request volume beyond casual use should attach Apify Proxy.

**Why are my `interest_over_time` values floats instead of clean integers?**
Only when you compared more than 5 terms - those rows went through the anchor-term
rescaling described above and carry a `rescaleFactor`. Rows from a single-batch run (<=5
terms) are always the exact integers Google returned.

**How is this priced?**
See [PRICING.md](PRICING.md) for the proposed pay-per-event plan.

# Actor input Schema

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

Up to 5 terms are compared directly in one request (Google Trends' own limit). More than 5 are automatically split into batches that share a common "anchor" term (see anchorTerm) so the 0-100 values stay comparable across batches. Leave empty to only fetch trendingNow.

## `anchorTerm` (type: `string`):

Only relevant with more than 5 searchTerms. This term is repeated in every batch and used to rescale the other batches onto the same 0-100 scale. Defaults to the first search term if left blank.

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

Two-letter country code (e.g. "US", "LV"), a region code (e.g. "US-CA"), or empty for worldwide.

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

Preset time range, or "custom" to use customTimeframeStart/customTimeframeEnd.

## `customTimeframeStart` (type: `string`):

Required when timeframe is "custom".

## `customTimeframeEnd` (type: `string`):

Required when timeframe is "custom".

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

Google Trends category ID to restrict results to (0 = All categories). See Google's published category list.

## `property` (type: `string`):

Which Google search vertical to pull trends from.

## `includeInterestOverTime` (type: `boolean`):

Search interest over time, as long-format rows (one per term per date).

## `includeInterestByRegion` (type: `boolean`):

Search interest broken down by region, as long-format rows (one per term per location).

## `regionResolution` (type: `string`):

Granularity for interest-by-region: COUNTRY (worldwide/multi-country geo), REGION (state/province within one country), CITY, or DMA (US media markets only).

## `includeRelatedQueries` (type: `boolean`):

Top and rising related search queries per term.

## `includeRelatedTopics` (type: `boolean`):

Top and rising related topics per term. Always issues one extra single-term request per term regardless of batching (Google Trends only returns this widget for single-term explores). See README limitations: Google's related-topics widget has been observed returning no data even for popular terms.

## `trendingNow` (type: `boolean`):

Fetch today's trending searches for trendingNowGeo (independent of searchTerms).

## `trendingNowGeo` (type: `string`):

Two-letter country code for the trending-now feed.

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

Google Trends throttles aggressively; using Apify Proxy (residential recommended for larger runs) spreads requests across IPs and makes retries far more likely to succeed. Optional for small/light runs.

## `maxRetries` (type: `integer`):

How many times to retry a single Trends request (with exponential backoff + a fresh session/identity) before giving up and recording an error for that piece of data.

## `requestDelayMs` (type: `integer`):

Politeness delay before each request (+/-25% jitter). Lower is faster but more likely to be throttled; raise it for large runs.

## Actor input object example

```json
{
  "searchTerms": [
    "apify",
    "web scraping"
  ],
  "geo": "",
  "timeframe": "today 12-m",
  "category": 0,
  "property": "",
  "includeInterestOverTime": true,
  "includeInterestByRegion": true,
  "regionResolution": "COUNTRY",
  "includeRelatedQueries": true,
  "includeRelatedTopics": false,
  "trendingNow": false,
  "trendingNowGeo": "US",
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxRetries": 5,
  "requestDelayMs": 1000
}
```

# Actor output Schema

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

No description

## `resultsAll` (type: `string`):

No description

# 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": [
        "apify",
        "web scraping"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gravelly_caladium/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": [
        "apify",
        "web scraping",
    ] }

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

```

## MCP server setup

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