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

Scrape Google Trends without the failures. Interest over time, interest by region, rising and breakout queries, keyword comparison, and up to 2,600+ trending searches per region with search volume.

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

## Pricing

Pay per event

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?

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

Pull **interest over time**, **interest by region**, **related and rising queries**, **related topics**, and **trending searches** from Google Trends — without the failures that plague other Trends scrapers.

### Why this one

Google Trends' internal API rejects requests from datacenter IPs with **HTTP 429 before it reads anything else**. Scrapers that skip the session handshake fail a large share of their runs — the most-used Google Trends Actor on Apify Store currently fails about **one run in four**.

This Actor performs the handshake Google expects, refreshes the session automatically when a request is throttled, and paces requests instead of bursting. In testing it completed **10 out of 10 keywords from a single datacenter IP with no proxy** — and for sustained high-volume runs it supports proxy rotation.

Concretely, it also gives you:

- **Rising and breakout queries** — the fastest-growing searches around your term, flagged when Google reports `Breakout` instead of a percentage. This is the data SEO and marketing teams actually act on.
- **Keyword comparison** on a shared 0–100 scale, exactly like the Trends UI (up to 5 terms).
- **Every search surface** — web, News, Images, YouTube, and Google Shopping.
- **Any geography** — worldwide, country, sub-region (`US-CA`), or metro (`US-NY-501`).
- **Trending searches at real depth** — up to **2,669** per region for a 7-day window (about 500 for 24h), each with an estimated search volume, growth figure, category and start time. Google's public RSS feed caps out at 10.
- **Paste a Google Trends URL** and the keywords, region, time range, category and surface are read straight off the link.

### Input

| Field | Type | Description |
|---|---|---|
| `keywords` | array | Search terms. Scraped separately unless `compareKeywords` is on |
| `startUrls` | array | Google Trends explore URLs — filters are read from the link |
| `compareKeywords` | boolean | Scrape all terms as one comparison on a shared scale (max 5) |
| `dataTypes` | array | `interestOverTime`, `interestByRegion`, `relatedQueries`, `relatedTopics` |
| `geo` | string | Empty for worldwide, or `US`, `GB`, `US-CA`, `US-NY-501` |
| `timeRange` | string | `now 7-d`, `today 12-m`, `today 5-y`, `all`, or `2024-01-01 2024-06-30` |
| `category` | integer | Google Trends category id (`0` = all, `71` = Food & Drink, `7` = Finance) |
| `property` | string | Web (default), `news`, `images`, `youtube`, `froogle` (Shopping) |
| `trendingNowGeos` | array | Also return current trending searches for these countries |
| `trendingTimeframeHours` | integer | Trending window: `4`, `24`, `48` or `168` (7 days) |
| `trendingCategoryIds` | array | Keep only these trending category ids |
| `trendingMaxItems` | integer | Cap trending searches per region (default `500`) |
| `trendingIncludeNews` | boolean | Attach news articles where Google's RSS feed overlaps |
| `maxItems` | integer | Stop after this many dataset items (`0` = no limit) |
| `concurrency` | integer | Parallel keyword requests (default `2`) |
| `requestDelayMs` | integer | Delay between requests (default `800`) |
| `proxyConfiguration` | object | Optional — the Actor works without a proxy |

#### Example

```json
{
  "keywords": ["protein powder", "creatine"],
  "dataTypes": ["interestOverTime", "interestByRegion", "relatedQueries"],
  "geo": "US",
  "timeRange": "today 12-m",
  "trendingNowGeos": ["US"]
}
```

### Output

One item per keyword:

```json
{
  "keyword": "protein powder",
  "geo": "US",
  "timeRange": "today 12-m",
  "property": "web",
  "averageInterest": 42.62,
  "peakInterest": { "date": "Apr 12, 2026", "value": 100 },
  "interestOverTime": [
    { "date": "Aug 10, 2025", "timestamp": 1754784000, "value": 33, "values": { "protein powder": 33 }, "isPartial": false }
  ],
  "interestByRegion": [
    { "geoCode": "US-WY", "geoName": "Wyoming", "value": 100, "values": { "protein powder": 100 } }
  ],
  "topQueries": [{ "query": "whey protein powder", "value": 100, "formattedValue": "100", "isBreakout": false }],
  "risingQueries": [{ "query": "bloom clear protein powder", "value": 3300, "formattedValue": "+3,300%", "isBreakout": false }],
  "scrapedAt": "2026-08-16T18:17:57.485Z"
}
```

With `trendingNowGeos`, trending items are emitted alongside:

```json
{
  "type": "trendingNow",
  "geo": "US",
  "rank": 1,
  "query": "rebel creamery",
  "searchVolume": 200000,
  "growthPercent": 1000,
  "categoryId": 10,
  "startedAt": "2026-08-16T00:20:00.000Z",
  "relatedTerms": ["rebel creamery llc", "rebel ice cream", "ice cream"],
  "news": []
}
```

Each keyword result also carries `hasData` — `false` when Google has no interest data for the
term, which it signals with a series of zeros rather than an error. In comparison mode,
`relatedByKeyword` holds the related queries for **every** term, not just the first.

Keywords that fail produce an item with `keyword` and `error`, so a failure is visible in your results rather than silently missing.

### Notes and limits

- **Trends values are relative, not absolute.** 100 is the peak within your chosen range and region — it is not a search volume. Comparing two separate runs is meaningless; use `compareKeywords` to put terms on one scale.
- **The final data point is usually partial.** It carries `isPartial: true` because the current period is still accumulating. Average and peak calculations exclude partial points.
- **Some terms have no related topics or queries.** Google simply has no data for low-volume terms; those fields are omitted rather than faked.
- **`relatedTopics` is frequently empty and is not enabled by default.** Google returns HTTP 200 with an empty list for many terms, because topics require the keyword to map to a Knowledge Graph entity — a broad phrase like "protein powder" often has none, while a named entity is more likely to. Enable it if you need it, but do not assume every keyword will populate it.
- **Comparison is capped at 5 keywords** by Google, not by this Actor.
- **Trending category ids are numeric.** Google does not publish the mapping, so each result carries its own `categoryId` — run once without a filter, see which ids matter for your niche, then filter on them. Ids observed in practice include 3, 4, 6, 9, 10, 11, 14, 15, 17, 18 and 20.
- **News articles come from a different Google feed** than the trending list, so only a few results will ever match. `trendingIncludeNews` is off by default to avoid the wasted request.
- **Rate limits are real at sustained volume.** A single IP running normal workloads is fine — 10 keywords in 34 seconds tested clean. Hammering Trends continuously for many minutes will eventually throttle that IP, and recovery takes several minutes. The Actor retries and re-establishes its session automatically, but for large or back-to-back runs enable `proxyConfiguration` so requests rotate across IPs.
- **If you see rate-limit warnings**, lower `concurrency` to 1 and raise `requestDelayMs`. Slower pacing avoids the retry entirely and is usually faster overall than backing off after a block.

### Local development

```bash
npm install
npm run build
node dist/main.js     # reads storage/key_value_stores/default/INPUT.json
```

# Actor input Schema

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

Search terms to analyse. Each is scraped separately unless "Compare keywords" is enabled.

## `startUrls` (type: `array`):

Paste explore URLs from the Google Trends UI — the keywords, region, time range, category and search surface are read straight out of the link.

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

Scrape all keywords as one comparison on a shared 0-100 scale, like the Google Trends UI. Maximum 5 keywords.

## `dataTypes` (type: `array`):

Each selected type is one extra request per keyword.

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

Empty for worldwide. Country code (`US`, `GB`, `IN`), sub-region (`US-CA`), or metro (`US-NY-501`).

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

One of `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 a custom range like `2024-01-01 2024-06-30`.

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

Google Trends category id. 0 means all categories (e.g. 71 = Food & Drink, 7 = Finance, 5 = Computers & Electronics).

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

Which Google property to measure interest on.

## `trendingNowGeos` (type: `array`):

Optional. Also return current trending searches for these country codes — with search volume, growth, category and start time. News articles are attached where Google publishes them.

## `trendingTimeframeHours` (type: `integer`):

Look-back window for trending searches. The Trends UI offers 4, 24, 48 and 168 (7 days); longer windows return far more results — roughly 80 for 4h, 500 for 24h and 2,600 for 168h.

## `trendingCategoryIds` (type: `array`):

Keep only these Google trending categories. Leave empty for all. Each result carries its own categoryId so you can discover the ids that matter to you.

## `trendingMaxItems` (type: `integer`):

Cap results per trending region.

## `trendingIncludeNews` (type: `boolean`):

Fetches Google's trending RSS feed to attach news articles. That feed is a different, much shorter list, so only a few results will match — leave off unless you specifically need article links.

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

Stop after this many dataset items. 0 means no limit.

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

Interface language for returned labels, e.g. `en-US`, `de`, `es`.

## `concurrency` (type: `integer`):

Parallel keyword jobs. Requests are rate-limited globally, so the delay below is the real throughput control; concurrency mainly helps overlap parsing.

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

Raise this if you see rate-limit warnings in the log.

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

Optional. This Actor works without a proxy, but one helps for very large runs.

## Actor input object example

```json
{
  "keywords": [
    "protein powder",
    "creatine"
  ],
  "startUrls": [],
  "compareKeywords": false,
  "dataTypes": [
    "interestOverTime",
    "relatedQueries"
  ],
  "geo": "",
  "timeRange": "today 12-m",
  "category": 0,
  "property": "",
  "trendingNowGeos": [
    "US",
    "GB"
  ],
  "trendingTimeframeHours": 24,
  "trendingCategoryIds": [],
  "trendingMaxItems": 500,
  "trendingIncludeNews": false,
  "maxItems": 0,
  "language": "en-US",
  "concurrency": 2,
  "requestDelayMs": 800,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

All scraped keywords and trending searches, newest run first.

# 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": [
        "protein powder"
    ]
};

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

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

```

## MCP server setup

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