# Google Trends Scraper: Compare Unlimited Keywords, Never Hangs (`granola2/google-trends-scraper`) Actor

Google Trends data that always finishes: any number of keywords on one scale, related queries, regions, trending now. Pay only for results.

- **URL**: https://apify.com/granola2/google-trends-scraper.md
- **Developed by:** [Jerry](https://apify.com/granola2) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 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?

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, Never Hangs

Google Trends data from a simple API call, with no Google account and no browser:

- **Interest over time** for any number of keywords, on **one shared 0-100 scale**.
- **Related queries** (top and rising, with "Breakout" flags) and **interest by region** (countries, sub-regions, cities or US metros).
- **Trending now**: today's trending searches for any country.

### Why this one

1. **It always finishes.** You set a hard time limit (`maxRunSeconds`). The run stops before it and keeps everything it got. No 20-minute runs that return nothing.
2. **You only pay for results.** Each dataset row costs $0.002 or less. Keywords that Google has no data for are listed in the run summary and cost nothing. An empty run costs nothing.
3. **Any number of keywords on one scale.** Google compares at most 5 terms at a time and rescales every request to its own 0-100. This Actor stitches the batches together, so 500 keywords come back directly comparable.

It is a working **pytrends alternative**: pytrends was archived in 2025 and now fails with 429 errors. This Actor handles sessions, retries and rate limits for you.

### One shared scale, explained

Google Trends only compares 5 keywords at once, and each request scales its own top value to 100. If you split 12 keywords into batches of 5 (what most Google Trends scrapers and pytrends scripts do), the numbers from different batches cannot be compared.

Real data, US, last 12 months, average interest:

| Keyword | Separate batches of 5 | This Actor (shared scale) |
| --- | ---: | ---: |
| ebay | 62 | 42.29 |
| wine | 62 | 42.78 |
| hoodie | 38 | 25.96 |
| wikipedia | 27 | 18.62 |
| podcast | 26 | 18.06 |
| matcha | 79 | 8.64 |
| violin | 32 | 3.56 |
| figma | 22 | 2.42 |
| badminton | 12 | 1.27 |
| uber app | 7 | 0.82 |
| costco jobs | 54 | 0.79 |
| bagel near me | 47 | 0.68 |

In separate batches, "costco jobs" (54) looks twice as popular as "wikipedia" (27). On the shared scale it is about 24 times smaller. The Actor finds this by putting overlapping keywords in each batch and chaining the ratios, with a confidence flag on each result.

### Input examples

**SEO keyword research: rank a keyword list by interest**

```json
{
  "keywords": ["running shoes", "trail running shoes", "barefoot shoes", "hiking boots", "walking shoes", "sneakers", "cross training shoes"],
  "geo": "US",
  "timeRange": "today 12-m",
  "reports": ["interestOverTime", "relatedQueries"]
}
```

**Brand comparison across regions**

```json
{
  "keywords": ["Nike", "Adidas", "Puma", "New Balance"],
  "geo": "GB",
  "timeRange": "today 5-y",
  "reports": ["interestOverTime", "interestByRegion"],
  "regionResolution": "city"
}
```

**Trending now in 3 countries** (no keywords needed)

```json
{ "trendingNow": ["US", "GB", "DE"] }
```

Other inputs: `property` (web, images, news, YouTube, shopping), `category`, `language`, `anchorKeyword`, `maxResults` (a spending cap), and `resolveTopics`, which looks each keyword up as a Google topic. With `resolveTopics`, "tennis" becomes the topic Tennis, which counts every spelling and language. You can also pass topic IDs such as `/m/07bs0` directly.

### Output

One dataset row per keyword per report, plus one row per trending search. Every keyword row has `report`, `keyword`, `geo`, `timeRange`, `category`, `property`, `trendsUrl` (opens the same view on Google Trends) and `fetchedAt`.

**interestOverTime**

```json
{
  "report": "interestOverTime",
  "keyword": "violin",
  "geo": "US",
  "timeRange": "today 12-m",
  "scale": "shared",
  "points": [{"date": "2025-11-02", "timestamp": 1762041600, "value": 3.82, "isPartial": false}],
  "peakValue": 5.09,
  "averageValue": 3.56,
  "stitch": {"anchor": "ebay", "hops": 2, "factor": 0.1157, "confidence": "medium"}
}
```

`scale` is `shared` (stitched across batches), `google` (5 or fewer keywords, one direct Google comparison) or `single` (`sameScale` off). `stitch.confidence` is `high` when the keyword was measured next to the anchor, `medium` or `low` as the chain of batches gets longer.

**relatedQueries**

```json
{
  "report": "relatedQueries",
  "keyword": "pickleball",
  "top": [{"query": "pickleball paddle", "value": 100}, {"query": "pickleball courts", "value": 87}],
  "rising": [{"query": "charles barkley pickleball comments", "value": 7300, "formattedValue": "Breakout"}]
}
```

**interestByRegion**

```json
{
  "report": "interestByRegion",
  "keyword": "padel",
  "resolution": "region",
  "regions": [{"geoCode": "US-FL", "geoName": "Florida", "value": 100}, {"geoCode": "US-NY", "geoName": "New York", "value": 75}]
}
```

Region values are relative within each keyword (the top region is 100).

**trendingNow**

```json
{
  "report": "trendingNow",
  "geo": "US",
  "rank": 1,
  "title": "conference league",
  "approxTraffic": "2000+",
  "startedAt": "2026-11-05T18:20:00Z",
  "news": [{"title": "Conference League line-ups: Atalanta vs. Kairat Almaty", "url": "https://sports.yahoo.com/articles/conference-league-line-ups-atalanta-164000981.html", "source": "Yahoo Sports"}]
}
```

### Cost

You pay per dataset row, plus a fraction of a cent per run start. Platform usage is included. Higher Apify plans pay less:

| Apify plan | Per 1,000 results |
| --- | ---: |
| Free | $2.00 |
| Bronze | $1.50 |
| Silver | $1.20 |
| Gold and above | $1.00 |

Examples on the Free plan:

| Run | Results | Cost |
| --- | ---: | ---: |
| 500 keywords, interest over time | 500 | $1.00 |
| 10 keywords, all 3 reports, plus trending now for 1 country | about 40 | about $0.08 |
| Trending now in 3 countries | about 30 | about $0.06 |

Set `maxResults` to cap what a run can cost.

### The SUMMARY record

Every run writes a `SUMMARY` record to its key-value store (Storage tab, then Key-value store). It is free and tells you what happened to every keyword:

```json
{
  "status": "completed",
  "keywordsRequested": 3,
  "resultsPushed": 9,
  "noData": [{"keyword": "xyzzy", "report": "relatedQueries", "reason": "google_returned_none"}],
  "failed": [],
  "notReached": [],
  "requests": 14,
  "retries": 2,
  "elapsedSeconds": 32
}
```

- `status`: `completed`, `stopped_at_time_limit` or `stopped_at_spending_cap`.
- `noData`: Google has nothing for that keyword and report. Not billed. `too_low_relative_to_set` means the keyword is too small next to the others to show on the shared scale. Run it with smaller keywords to see it.
- `failed`: Google kept refusing after retries. Not billed. Run these again.
- `notReached`: the time limit or spending cap came first. Give them a new run or a higher `maxRunSeconds`.

### Use from AI agents (MCP)

AI agents can call this Actor through [Apify's MCP server](https://mcp.apify.com). The inputs have short, clear descriptions and small defaults, so agents can use it without extra prompting. Example config for Claude Desktop, Cursor or any MCP client:

```json
{
  "mcpServers": {
    "apify": {
      "command": "npx",
      "args": ["-y", "@apify/actors-mcp-server", "--actors", "granola2/google-trends-scraper"],
      "env": { "APIFY_TOKEN": "<your Apify API token>" }
    }
  }
}
```

### FAQ

**What does 0-100 mean?** Relative interest, not search counts. 100 is the highest point in the set of keywords and time range you asked for. 50 means half as many searches as that peak. Google does not publish absolute search volumes.

**Why do values shift slightly between runs?** Google computes Trends from a sample of searches and refreshes the sample. Two runs a day apart can differ by a few points, more for small keywords. That is Google's data, not a stitching error.

**Where are related topics?** Since 2026 Google returns empty related topics to every automated tool, including this one, so the Actor does not offer that report and does not charge for empty rows. Related queries still work. To get topic-level data, turn on `resolveTopics` or pass topic IDs as keywords.

**How is this different from Google's official Trends API?** Google's Trends API is in alpha, for approved testers only, and has its own quotas. This Actor works today for anyone, adds the shared scale for any number of keywords, trending now and region detail, and charges only for results.

**Does it need my Google account?** No. It uses the public Google Trends website endpoints, the same data you see on trends.google.com.

***

If it worked for you, a rating helps others find it.

# Actor input Schema

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

Search terms or Google topic IDs (like /m/0dl567) to look up. Any number up to 2,000; more than 5 are put on one shared 0-100 scale automatically.

## `resolveTopics` (type: `boolean`):

Query each keyword as its Google topic (for example 'tennis' becomes Tennis, Sports, /m/07bs0) instead of the exact search term. Topics group spellings and languages. Costs one extra lookup per keyword.

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

Country code (US) or sub-region code (US-NY). Leave empty for worldwide.

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

A preset such as 'today 12-m' or 'now 7-d', or a custom range written as 'YYYY-MM-DD YYYY-MM-DD'.

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

Google Trends category ID. 0 means all categories.

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

Which Google search to measure: web, images, news, YouTube or shopping.

## `reports` (type: `array`):

Which reports to return for each keyword. Each keyword-report pair is one billed result.

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

Level of detail for interest by region. Auto picks one level below the location: countries worldwide, sub-regions in a country, cities in a sub-region. Metro (DMA) is US only.

## `sameScale` (type: `boolean`):

With 2 or more keywords, return interest over time on one shared 0-100 scale so values compare across keywords.

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

Optional. One of your keywords to use as the reference when stitching more than 5 keywords. Leave empty to choose automatically.

## `trendingNow` (type: `array`):

2-letter country codes. Returns today's trending searches for each country. Works with or without keywords.

## `maxRunSeconds` (type: `integer`):

Hard time limit. The run stops before it and returns everything finished so far.

## `maxResults` (type: `integer`):

Optional. Stop after this many billed results.

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

Interface language for Google's labels, such as en-US or de-DE.

## Actor input object example

```json
{
  "keywords": [
    "pickleball",
    "padel",
    "tennis"
  ],
  "resolveTopics": false,
  "geo": "US",
  "timeRange": "today 12-m",
  "category": 0,
  "property": "web",
  "reports": [
    "interestOverTime",
    "interestByRegion",
    "relatedQueries"
  ],
  "regionResolution": "auto",
  "sameScale": true,
  "anchorKeyword": "",
  "trendingNow": [],
  "maxRunSeconds": 300,
  "language": "en-US"
}
```

# Actor output Schema

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

One row per keyword per report, plus one row per trending search.

## `summary` (type: `string`):

What happened to every keyword: no data, failed, not reached. Not billed.

# 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": [
        "pickleball",
        "padel",
        "tennis"
    ],
    "geo": "US",
    "reports": [
        "interestOverTime",
        "interestByRegion",
        "relatedQueries"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("granola2/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": [
        "pickleball",
        "padel",
        "tennis",
    ],
    "geo": "US",
    "reports": [
        "interestOverTime",
        "interestByRegion",
        "relatedQueries",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("granola2/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": [
    "pickleball",
    "padel",
    "tennis"
  ],
  "geo": "US",
  "reports": [
    "interestOverTime",
    "interestByRegion",
    "relatedQueries"
  ]
}' |
apify call granola2/google-trends-scraper --silent --output-dataset

```

## MCP server setup

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