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

Scrapes Google Trends interest over time, interest by region and related/rising searches. States what the 0-100 numbers actually mean: they rescale per request, so every row carries the keyword set it was scaled against, and rising searches keep their growth percent separate.

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

## Pricing

from $0.84 / 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.
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, Regions & Rising

Reads Google Trends' own two-stage API: interest over time, interest by region,
and related/rising searches. No key, no auth.

### What you get

| `recordType` | One per | Carries |
| --- | --- | --- |
| `TIMESERIES_POINT` | keyword × time point | value, ISO date, unix timestamp, Google's own resolution, `isPartial` |
| `REGION` | keyword × region | region code and name, value, which keyword won that region |
| `RELATED_SEARCH` | related query/topic | `listType` (TOP or RISING), rank, the 0-100 index **or** the growth percent, and a breakout flag |
| `SEARCH_SUMMARY` | request group | the resolved date window, the resolution Google chose, which widgets existed vs were fetched, and the scaling basis |
| `ERROR` | failed input | a named reason — every input maps to at least one row |

### Read this before you use the numbers

**A Google Trends value is not search volume.** It is *percent of the peak
within that one request*, and it rescales when the request changes. Measured,
same keyword, same region, same period:

```
ethereum alone              max = 100   series starts 100, 70, 60, 66 ...
ethereum beside "bitcoin"   max =  10   series starts  10,  7,  6,  7 ...
```

A tenfold difference, purely because another keyword joined the request and
moved the peak. **Two runs are comparable only if the keyword set is
identical.** Every value row therefore carries `scalingBasis` (the exact set the
0-100 scale was computed against), `comparedWith`, and `valuesAreRelative`.

That is also why **Compare keywords** is a real choice, not a convenience:

- **off** (default) — one request per keyword, each series scaled to its own
  peak. Good for "how did each term move over time". Values are **not**
  comparable between keywords.
- **on** — all keywords in one request on one shared scale, so they *are*
  comparable. Google allows at most 5 and returns HTTP 400 for more.

**The last data point is incomplete.** The trailing period is the current,
unfinished week/day/hour and is flagged `isPartial`. Charted as-is it produces
a cliff at the right edge that reads like a collapse in interest. The summary
counts them.

**Related searches mix two units.** Google returns TOP and RISING in one
unlabelled array: TOP values are a 0-100 index (3–100 in a measured response),
RISING values are **percent growth** (160–29,050 in the same response), and
anything above roughly 5,000% is labelled the literal string `Breakout`.
Flattened together they are meaningless. Here they are named by `listType`,
with the index in `value`, the growth in `growthPercent`, and `isBreakout` as
its own flag — never sharing a column.

### Inputs this actor refuses on purpose

- **An unknown `category` or `property`.** Google's *first* call accepts these
  with HTTP 200 and a complete, healthy-looking widget payload; only the second
  call refuses. A client that validates on the first response believes the query
  was accepted and then sees what looks like a network error. Both are checked
  up front here.
- **More than 5 compared keywords** — Google returns HTTP 400. The error names
  the alternative (turn comparison off) and its consequence.
- **An unrecognised `timeRange`** — likewise a Google 400.

A bogus `geo` is refused by Google itself, so a typo fails loudly instead of
quietly returning worldwide data.

### Notes

- Google picks the data **resolution** from your time range (minute, 8-minute,
  hourly, daily, weekly, monthly) and reports it back. It decides what one point
  means, so it is recorded on every row rather than assumed.
- Multi-keyword comparisons change the widget set: Google drops the
  `RELATED_TOPICS` widget entirely and returns per-keyword
  `GEO_MAP_n` / `RELATED_QUERIES_n` instead. `widgetsAvailable` records what
  actually existed.
- **Google Trends rate-limits by IP** and answers HTTP 429 rather than
  throttling. Rotating fingerprints does not help — only waiting, or a
  different exit address. Keep `minRequestInterval` at 2s or higher, and turn on
  the Residential proxy for anything large.

# Actor input Schema

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

Search terms to look up. With 'Compare keywords' off each term is scraped in its own request and scaled to its own peak; with it on they share one 0-100 scale (Google allows at most 5 per comparison).

## `compareKeywords` (type: `boolean`):

OFF (default): one request per keyword, each series scaled to its own peak — values are NOT comparable between keywords. ON: all keywords in one request sharing a single 0-100 scale, so they ARE comparable. Max 5 keywords; Google returns HTTP 400 for more.

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

Two-letter country code (US, GB, ID), a subregion (US-CA), or leave empty for worldwide. An unrecognised code is refused by Google with HTTP 400, so a typo fails loudly rather than silently returning worldwide data.

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

Google picks the data resolution from this and reports it back — minute, hourly, daily, weekly or monthly. The resolution decides what one data point means, so it is recorded on every row.

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

Google Trends category id (0 = all categories, 7 = finance, 5 = computers & electronics...). A real category genuinely changes the data. An INVALID id is not refused by the first API call — it returns HTTP 200 with a full payload and only fails one call later.

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

Which Google surface to measure. An unknown value passes the first call and fails the second, so this actor validates it up front.

## `includeTimeseries` (type: `boolean`):

One row per keyword per time point. The final point is the current unfinished period and is flagged isPartial.

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

One row per keyword per region, with the region code and name.

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

Top queries (a 0-100 index) and rising queries (percent growth, with Google's 'Breakout' label kept as its own flag).

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

Same shape as related queries, but Google Knowledge Graph topics. Note Google omits this widget entirely for multi-keyword comparisons.

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

Interface language. Affects region names and formatted labels, not the numbers.

## `timezoneOffset` (type: `string`):

Minutes offset Google uses to bucket time points, as its own web app sends it. -420 is UTC-7.

## `maxConcurrency` (type: `integer`):

Kept at 1 by default: Google Trends rate-limits by IP and answers HTTP 429 rather than throttling.

## `minRequestInterval` (type: `integer`):

Politeness pacing shared across all workers. This is a metered public endpoint, not a bulk feed.

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

Optional and OFF by default. Google Trends rate-limits by IP, so Residential is the difference between a large run finishing and not.

## Actor input object example

```json
{
  "keywords": [
    "bitcoin",
    "ethereum"
  ],
  "compareKeywords": false,
  "geo": "US",
  "timeRange": "today 12-m",
  "category": 0,
  "property": "",
  "includeTimeseries": true,
  "includeRegions": true,
  "includeRelatedQueries": true,
  "includeRelatedTopics": true,
  "language": "en-US",
  "timezoneOffset": "-420",
  "maxConcurrency": 1,
  "minRequestInterval": 2,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/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"] }

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

```

## MCP server setup

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