# Google Trends Scraper - Compare Unlimited Keywords (`roundish_wildlife/google-trends-unlimited-keywords`) Actor

Scrape Google Trends interest over time, interest by region and related queries. Compare more than 5 keywords on one common 0-100 scale. Clean flat output, automatic retries and proxy rotation.

- **URL**: https://apify.com/roundish_wildlife/google-trends-unlimited-keywords.md
- **Developed by:** [OIa Martin Torheim](https://apify.com/roundish_wildlife) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 keyword reports

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 - Compare Unlimited Keywords

Get Google Trends data for **any number of keywords on one comparable 0-100 scale**, in a clean flat table you can open straight in Excel, Google Sheets, Make, n8n or Zapier.

For each keyword you can get:

- 📈 **Interest over time**: one row per keyword per date
- 🗺️ **Interest by region**: countries, regions, cities or US metros (DMA)
- 🔎 **Related queries**: top and rising searches, including "Breakout" terms
- 🧩 **Related topics**: top and rising topics (brands, products, people, places). Off by default: Google Trends currently returns no related-topics data for most searches (empty results are never charged)
- 📊 **Summary row** (free): average, peak, peak date, latest value and trend slope

### Why use this Google Trends scraper?

| | This Actor | Google Trends website / typical scrapers |
|---|---|---|
| Keywords compared on one scale | **Unlimited (up to 100 per run)** | Max 5 |
| Output format | **Flat rows**, one `type` column | Nested JSON widgets |
| Rate limits (HTTP 429) | **Automatic retries, exponential backoff, new IP per retry, optional residential fallback** | Run crashes or returns empty |
| One keyword fails | **That keyword gets an `error` row, the rest of the run continues** | Whole run fails |
| You pay for | **Only keyword reports that returned data** | Often every attempt |
| Python `pytrends` | Not needed (it was archived in April 2025) | - |

#### How "more than 5 keywords" works

Google Trends only compares 5 terms at a time and scales every request separately, so numbers from two different comparisons can't normally be compared. This Actor splits your keywords into batches of 4 and adds one shared **anchor keyword** to every batch. Because the anchor appears in every batch, its values show exactly how each batch's scale relates to the others. All batches are rescaled to the first batch's scale, then everything is normalised so the single highest point across all your keywords is 100.

- `value` is the number on the common scale (use this one).
- `rawValue` is the number Google returned in that batch.
- `batch` and `anchorKeyword` show how each row was produced.

**Tip:** choose an anchor with steady interest and roughly similar popularity to your keywords. If the anchor is far more popular than a keyword, that keyword's values become coarse, and the run log warns you. If you leave the anchor empty, your first keyword is used.

### Use cases

- **E-commerce and print-on-demand sellers**: compare 20+ product ideas at once and find seasonal peaks.
- **SEO and content teams**: rank topic ideas by real search interest and find rising queries.
- **Market research**: compare brands, products or competitors across countries.
- **Data and AI pipelines**: flat, typed rows that load straight into a spreadsheet, database or LLM.

### Input

Only `keywords` is required. Everything else has sensible defaults.

| Field | Default | Description |
|---|---|---|
| `keywords` | - | Up to 100 search terms |
| `timeframe` | `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`, or `custom` |
| `startDate`, `endDate` | - | Used when `timeframe` is `custom` (YYYY-MM-DD, from 2004-01-01) |
| `geo` | worldwide | Country code (`US`, `GB`, `NO`) or sub-region (`US-CA`) |
| `category` / `customCategoryId` | `0` (all) | Google Trends category ID, e.g. `71` Food & Drink, `18` Shopping |
| `property` | web | `""` web, `youtube`, `news`, `images`, `froogle` (Google Shopping) |
| `includeInterestOverTime`, `includeInterestByRegion`, `includeRelatedQueries`, `includeRelatedTopics` | `true` (related topics: `false`) | Choose which datasets to fetch (and pay for) |
| `includeSummary` | `true` | Free summary row per keyword |
| `anchorKeyword` | first keyword | Shared keyword used for rescaling across batches |
| `regionResolution` | automatic | `COUNTRY`, `REGION`, `CITY` or `DMA` |
| `proxyConfiguration` | Apify Proxy (automatic) | Use the RESIDENTIAL group if you see many blocks |
| `residentialFallback` | `true` | Switch to residential proxy automatically after repeated blocks |
| `maxRetries`, `requestDelayMillis` | `5`, `1000` | Retry and pacing settings |

#### Example: compare 8 product ideas in the US over 5 years (time series only)

```json
{
    "keywords": ["dubai chocolate", "matcha", "labubu", "protein coffee", "mushroom coffee", "cottage cheese", "bubble tea", "kombucha"],
    "anchorKeyword": "coffee",
    "timeframe": "today 5-y",
    "geo": "US",
    "includeInterestByRegion": false,
    "includeRelatedQueries": false,
    "includeRelatedTopics": false
}
```

#### Example: rising YouTube searches in Norway for a custom period

```json
{
    "keywords": ["strikking", "hytte"],
    "timeframe": "custom",
    "startDate": "2025-01-01",
    "endDate": "2025-12-31",
    "geo": "NO",
    "property": "youtube",
    "includeInterestOverTime": false,
    "includeInterestByRegion": false,
    "includeRelatedTopics": false
}
```

### Output

The examples below are illustrative. Every row has `type`, `keyword`, `geo`, `timeframe`, `category`, `property` and `scrapedAt`. Filter or split the dataset by `type`.

**`interest_over_time`**, one row per keyword per date:

```json
{
    "type": "interest_over_time",
    "keyword": "matcha",
    "geo": "US",
    "timeframe": "today 12-m",
    "category": 0,
    "property": "web",
    "scrapedAt": "2026-09-30T10:05:30Z",
    "date": "2025-10-05",
    "timestamp": 1759622400,
    "formattedTime": "Oct 5 - 11, 2025",
    "value": 41.33,
    "rawValue": 62,
    "isPartial": false,
    "anchorKeyword": "coffee",
    "batch": 2,
    "scaledAcrossBatches": true
}
```

`date` is `YYYY-MM-DD` for daily, weekly and monthly data, and a full UTC timestamp (`2025-10-05T14:00:00Z`) for hourly data. `isPartial: true` marks the current, incomplete period.

**`summary`**, one row per keyword (free):

```json
{ "type": "summary", "keyword": "matcha", "average": 38.2, "peakValue": 55.1, "peakDate": "2025-12-21", "latestValue": 41.33, "trendSlope": 0.2818, "dataPoints": 52 }
```

`trendSlope` is the linear trend in value points per period (positive = growing).

**`interest_by_region`**:

```json
{ "type": "interest_by_region", "keyword": "matcha", "rank": 1, "geoCode": "US-HI", "geoName": "Hawaii", "value": 100, "hasData": true, "resolution": "REGION" }
```

Region values are Google's per-keyword scale (100 = the region where that keyword is most popular relative to all searches there).

**`related_query`** and **`related_topic`**:

```json
{ "type": "related_query", "keyword": "matcha", "rankType": "rising", "rank": 1, "query": "matcha latte recipe", "value": 4450, "formattedValue": "+4,450%", "isBreakout": false, "link": "https://trends.google.com/trends/explore?q=matcha+latte+recipe&date=today+12-m" }
{ "type": "related_topic", "keyword": "matcha", "rankType": "top", "rank": 1, "topicTitle": "Green tea", "topicType": "Beverage", "topicMid": "/m/02b_6", "value": 100, "formattedValue": "100", "isBreakout": false }
```

**`error`**: if a keyword or dataset still fails after all retries, you get a row like this instead of a crashed run (these rows are never charged):

```json
{ "type": "error", "keyword": "matcha", "dataset": "interestByRegion", "errorMessage": "Failed after 6 attempts: HTTP 429 Too Many Requests (rate limited by Google)" }
```

A run summary (reports, errors, retries, proxy used) is saved as `OUTPUT` in the run's key-value store.

### Pricing

This Actor uses **pay-per-event** pricing:

| Event | Price | When |
|---|---|---|
| `keyword-report` | **$0.002** ($2 per 1,000) | One keyword × one dataset that returned data |
| Actor start | Apify default | Once per run |

Examples:

- 10 keywords, interest over time only = 10 reports = **$0.02**
- 10 keywords, all 4 datasets = 40 reports = **$0.08**
- 100 keywords, interest over time only = **$0.20**

You are **not** charged for the anchor keyword (unless it is also in your list), for summary rows, for error rows or for datasets where Google had no data. Set **Maximum cost per run** in the run options and the Actor stops cleanly when it is reached.

### FAQ

**Why do my values differ from the Google Trends website?**
Google Trends samples its data, so values can change slightly between requests. With more than 5 keywords the values are also rescaled to a common scale; `rawValue` holds Google's original number.

**I get many 429 / "rate limited" errors.**
Google Trends limits traffic heavily. Set the proxy to the **RESIDENTIAL** group, increase `requestDelayMillis`, or split very large jobs into several runs.

**Can I use this without a proxy?**
Yes (turn Apify Proxy off), but you will be rate-limited much sooner.

**Does it support "Trending now" / daily trending searches?**
Not in this version. It is on the roadmap.

**Can I call it from Make, n8n, Zapier or my own code?**
Yes. Use the Apify integrations or API. Each row is flat, so it maps directly to spreadsheet columns.

### Limits

- Up to 100 keywords per run (split larger jobs into several runs).
- Region, related queries and related topics are returned per keyword (Google's own per-keyword scale). Only interest over time is rescaled across batches.
- Google Trends data is sampled and relative, not absolute search volume.
- Very low-volume keywords may return no data (not charged).
- Google can change its internal endpoints at any time. We monitor the Actor daily and fix breakages quickly. Please report problems in the Issues tab.

### Is it legal to scrape Google Trends?

This Actor collects only **public, aggregated and anonymised** statistics that Google Trends shows to any visitor. It does not collect personal data, log in, or bypass any paywall. You are responsible for using the data in line with applicable law and Google's terms of service. If in doubt, consult a lawyer. Read more in Apify's post [Is web scraping legal?](https://blog.apify.com/is-web-scraping-legal/).

This Actor is not affiliated with or endorsed by Google.

# Changelog

This Actor's version history is a separate document: https://apify.com/roundish_wildlife/google-trends-unlimited-keywords/changelog.md

# Actor input Schema

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

Search terms to analyse, one per line. Up to 100 per run. Unlike Google Trends itself (max 5), all keywords are put on one comparable 0-100 scale using a shared anchor keyword.

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

Period to analyse. Choose 'Custom date range' to use the start and end dates below. Short timeframes (past hour to past 7 days) return hourly or minute data; longer ones return daily, weekly or monthly data.

## `startDate` (type: `string`):

First day of the custom range (YYYY-MM-DD, not earlier than 2004-01-01). Used only when Timeframe is 'Custom date range'.

## `endDate` (type: `string`):

Last day of the custom range (YYYY-MM-DD). Used only when Timeframe is 'Custom date range'.

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

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

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

Limit results to a Google Trends category. Use 'Custom category ID' below for any other category.

## `customCategoryId` (type: `integer`):

Optional numeric Google Trends category ID (the 'cat' parameter in a Google Trends URL). Overrides the Category selection.

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

Which Google search to analyse.

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

One row per keyword per date with a value on a common 0-100 scale.

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

Where each keyword is most popular (countries worldwide, or regions/cities inside a country).

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

Top and rising related search queries, including 'Breakout' terms.

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

Top and rising related topics (entities such as brands, products or people). Note: Google Trends currently returns no related-topics data for most searches, so this is off by default. You are never charged for empty results.

## `includeSummary` (type: `boolean`):

Adds one 'summary' row per keyword with average, peak value, peak date, latest value and trend slope. Free (part of the interest-over-time report).

## `anchorKeyword` (type: `string`):

Used to put more than 5 keywords on one scale: it is added to every batch of 4 keywords and used to rescale. Leave empty to use your first keyword. Best results: a keyword with steady interest and similar popularity to your keywords. The anchor is not billed unless it is also in your keyword list.

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

Level of detail for interest by region. 'Automatic' = countries for worldwide, regions for a country. City and DMA (US metro) need a country in 'Country or region'.

## `includeLowVolumeRegions` (type: `boolean`):

Also return regions where Google has too little data (value 0, hasData false).

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

Interface language used for topic names and formatted dates, e.g. en-US, de-DE, nb-NO.

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

Google Trends rate-limits heavily. Apify Proxy (automatic) is used by default. If you see many blocks, choose the RESIDENTIAL group.

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

If Google keeps blocking the default proxy, automatically switch to Apify residential proxy for the rest of the run (if your plan includes it).

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

How many times a failed or rate-limited request is retried (with exponential backoff and a new IP) before the keyword is reported as an error.

## `requestDelayMillis` (type: `integer`):

Pause between requests to avoid rate limits.

## Actor input object example

```json
{
  "keywords": [
    "coffee",
    "tea",
    "matcha"
  ],
  "timeframe": "today 12-m",
  "geo": "US",
  "category": "0",
  "property": "",
  "includeInterestOverTime": true,
  "includeInterestByRegion": true,
  "includeRelatedQueries": true,
  "includeRelatedTopics": false,
  "includeSummary": true,
  "regionResolution": "auto",
  "includeLowVolumeRegions": false,
  "language": "en-US",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "residentialFallback": true,
  "maxRetries": 5,
  "requestDelayMillis": 1000
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "keywords": [
        "coffee",
        "tea",
        "matcha"
    ],
    "timeframe": "today 12-m",
    "geo": "US",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("roundish_wildlife/google-trends-unlimited-keywords").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": [
        "coffee",
        "tea",
        "matcha",
    ],
    "timeframe": "today 12-m",
    "geo": "US",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("roundish_wildlife/google-trends-unlimited-keywords").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": [
    "coffee",
    "tea",
    "matcha"
  ],
  "timeframe": "today 12-m",
  "geo": "US",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call roundish_wildlife/google-trends-unlimited-keywords --silent --output-dataset

```

## MCP server setup

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

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/k7O4Ct0j9vdTosDM9/builds/zfPi60aWeTZ9Z2H9J/openapi.json
