# Google Trends Scraper API: Bulk, Compare, YouTube (`nimait/google-trends-scraper`) Actor

Scrape Google Trends keyword data as JSON or CSV: interest over time, interest by region down to US metro and city, and related queries. Compare up to 5 keywords on one shared 0-100 scale, or bulk-scrape an unlimited list. Web, Images, News, Shopping, YouTube. No start fee; empty runs are free.

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

## Pricing

$4.00 / 1,000 per trends data rows

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

This **Google Trends scraper** pulls **Google Trends** keyword data as clean JSON or CSV —
**interest over time**, **interest by region** down to US metro and city level, and **related
queries** — for a single keyword or for a list of thousands. It talks to the same JSON endpoints the
Google Trends website itself calls, so there is **no headless browser and no DOM parsing**. That is
the whole reason the older generation of Google Trends scrapers breaks every time Google reshuffles
its page markup, and this one does not.

You get one self-describing row per keyword per data surface, a genuinely co-normalized comparison
when you ask for one, and **no charge at all for a run that comes back empty**.

### What data can this Google Trends scraper extract?

| Data surface           | What you get                                                                               |
| ---------------------- | ------------------------------------------------------------------------------------------ |
| **Interest over time** | The trend line: one 0–100 value per time bucket per keyword, plus the period average.      |
| **Interest by region** | Where the searches come from — country, subregion, US metro area (Nielsen DMA), or city.   |
| **Related queries**    | What people also searched, split into **top** and **rising**, with Google's Breakout flag. |
| **Search types**       | Web, Images, News, Google Shopping and **YouTube search** — five separate trend curves.    |

#### Google Trends interest over time

The familiar Trends line chart, as data. Every bucket carries its ISO timestamp, its epoch seconds,
Google's own human-readable label, the value per keyword, a `hasData` flag per keyword, and an
`isPartial` flag on the trailing bucket. The bucket size follows the range you ask for: hourly for
the `now` presets, daily up to about nine months, weekly for a year, monthly for five years and up.

Google omits the period averages for single-keyword lookups. This Actor computes them the same way
Google does — the mean of every bucket including the trailing partial one, rounded — and marks them
`averagesComputed: true` so you always know whether a number came from Google or from us.

#### Google Trends interest by region: country, metro (DMA) and city

Set the granularity you want. A US run at metro granularity returns **all 210 Nielsen DMAs** with
their codes (`759 Cheyenne WY-Scottsbluff NE`, `744 Honolulu HI`), which is the level most demand
and media-planning work actually needs. City granularity returns fewer rows by design — Google only
reports places with enough search volume to be statistically meaningful.

#### Google Trends related queries: top and rising

Top queries are the most common over your period; rising queries are the fastest growing. When
Google reports **"Breakout"** it means growth above 5000% and it stops publishing a real percentage,
so those entries are flagged `isBreakout: true` — sort on that flag first, then on value.

#### YouTube, News, Shopping and Images search trends

The same keyword often behaves nothing like its web curve on YouTube. Five search types are
supported and each returns a distinct series:

| Search type         | What it measures                   | Status                    |
| ------------------- | ---------------------------------- | ------------------------- |
| Web search          | The familiar Google Trends numbers | Verified                  |
| **YouTube search**  | Searches inside YouTube            | Verified — distinct curve |
| **News search**     | Searches inside Google News        | Verified — distinct curve |
| **Image search**    | Searches inside Google Images      | Supported                 |
| **Google Shopping** | Searches inside Google Shopping    | Supported                 |

### Compare keywords, or bulk-scrape a keyword list

These are the two modes, and picking the right one is the single most important thing to understand
about Google Trends data.

- **Bulk mode (default).** Put a whole keyword list in. Each keyword gets its own lookup and its own
  0–100 scale, where **100 is that keyword's own peak**. There is no cap on how many keywords you
  send. This is what you want for keyword research across a list.
- **Comparison mode.** Switch it on and up to 5 keywords go to Google in **one** request, so the
  numbers come back on a **single shared 0–100 scale** — the way the Trends website shows them.
  Scrapers that fetch each keyword separately and staple the results together produce numbers that
  simply are not comparable.

#### Why the two modes give different numbers

Real figures from two runs of this Actor on 9 August 2026, both `coffee` and `matcha`, US, past 12
months:

| Mode                                         | coffee (period average) | matcha (period average) |
| -------------------------------------------- | ----------------------- | ----------------------- |
| **Bulk** (`compare: false`)                  | 75                      | 72                      |
| **Comparison** (`compare: true`, with `tea`) | 75                      | **6**                   |

Nothing is broken. Google's index is always relative to 100. In bulk mode, `matcha` averaging 72
means "matcha in an average week runs at 72% of matcha's own best week" — it says nothing about how
matcha compares to coffee. In comparison mode, 100 is the busiest week of the busiest keyword in the
set, so matcha gets crushed to 6 against coffee. That is the correct answer to "which of these is
bigger", and it is exactly what the Trends website shows.

Rows make the distinction explicit: `comparisonGroup: "coffee | tea | matcha"` in comparison mode,
and **`comparisonGroup: null`** in bulk mode, which tells you those rows are *not* comparable with
each other.

### Google Trends for keyword research, SEO and market research

People use this Google Trends scraper for:

- **Keyword research** — size demand across a keyword list, then sort by period average.
- **Rising and breakout queries** — find what is growing before it shows up in volume tools.
- **Seasonality** — pull five years of monthly data and read the annual shape before you buy stock
  or schedule content.
- **Brand and competitor comparison** — put up to five brands on one shared scale.
- **Market research** — track a category month by month, or use search interest as a demand proxy.
- **Regional targeting** — find the metros where a term over-indexes.
- **Disambiguation** — restrict to a Trends category to separate "jaguar" the car (47) from
  "jaguar" the animal (66).

Search interest is a *relative index*, not a search volume. It pairs well with a volume/CPC tool
rather than replacing one.

### How to scrape Google Trends step by step

1. Click **Try for free** and sign in to Apify.
2. Type your search terms into **Keywords**, one per line.
3. Set **Location** (empty means worldwide) and **Time range** (`today 12-m` is the default).
4. Leave **Compare keywords** off to scrape a list; switch it on to put 2–5 terms on one scale.
5. Tick only the data surfaces you need. Interest over time is on by default; every extra surface is
   an extra request to Google.
6. Click **Start**. A two-keyword trend-line run takes about three seconds.
7. Open the **Output** tab and export, or pull the dataset from the API.

Start with two or three keywords and interest over time only, so you can see the output shape before
committing a long list.

### Input

Only `keywords` is required. A realistic run — five product keywords across the US for the past
year, with a regional breakdown by metro area:

```json
{
    "keywords": ["air fryer", "electric bike", "padel", "protein powder", "matcha"],
    "geo": "US",
    "time": "today 12-m",
    "includeTimeseries": true,
    "includeGeo": true,
    "geoResolution": "dma"
}
```

| Setting                 | What it does                                                                                                                             |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **Keywords**            | One search term per line. No limit in bulk mode; 2–5 in comparison mode.                                                                 |
| **Compare keywords**    | Off by default. On puts every keyword on one shared scale.                                                                               |
| **Interest over time**  | The trend line. On by default; this is the surface most people want.                                                                     |
| **Interest by region**  | Where the searches come from. Off by default. One extra request per lookup.                                                              |
| **Related queries**     | What people also searched, top and rising. Off by default. One extra request per keyword.                                                |
| **Related topics**      | Off by default and **currently returns nothing from Google** — see Limitations. Never charged for.                                       |
| **Location**            | Empty for worldwide, or `US`, `GB`, `DE`, `US-NY`, `GB-ENG`.                                                                             |
| **Time range**          | A preset, a `yyyy-mm-dd yyyy-mm-dd` range, or an hourly `yyyy-mm-ddThh` range. Validated before any request.                             |
| **Search type**         | Web, Images, News, Google Shopping or YouTube search.                                                                                    |
| **Region granularity**  | Automatic, country, subregion, metro area (DMA, US only) or city.                                                                        |
| **Category**            | Restrict to one Trends category id — the clean way to separate "jaguar" the car (47) from "jaguar" the animal (66).                      |
| **Proxy configuration** | Datacenter proxy by default, which is included in every Apify plan and is what we measured as fastest. Residential is one dropdown away. |
| **Time zone offset**    | Minutes from UTC. `0` keeps results reproducible between runs — usually what you want for a dataset.                                     |
| **Max retries**         | Retries wait about 2s, 6s and 15s, and switch to a fresh IP and cookie jar from the second retry onwards.                                |

Bad time ranges are rejected **before a single request goes out**, so a typo costs you nothing.

### Output: export to JSON, CSV, Excel or the API

One row per keyword per surface, each row self-describing — it repeats the query it answers, so rows
stay readable on their own after export. Download as **JSON, CSV, Excel, XML or HTML**, or pull the
dataset straight from the Apify API. These are **real rows from real runs on 9 August 2026**, trimmed
for readability.

**Interest over time** — comparison of `coffee`, `tea`, `matcha` in the US. `values` is index-aligned
to `keywords`, so `values[2]` is always matcha:

```json
{
    "keywords": ["coffee", "tea", "matcha"],
    "comparisonGroup": "coffee | tea | matcha",
    "geo": "US",
    "time": "today 12-m",
    "searchType": "web",
    "category": 0,
    "fetchedAt": "2026-08-09T14:24:53.464Z",
    "surface": "timeseries",
    "resolution": "WEEK",
    "averages": [75, 36, 6],
    "averagesComputed": false,
    "points": [
        {
            "time": "2025-08-03T00:00:00.000Z",
            "epochSeconds": 1754179200,
            "formattedTime": "Aug 3 – 9, 2025",
            "values": [68, 35, 7],
            "hasData": [true, true, true],
            "isPartial": false
        }
    ]
}
```

**Interest by region** at US metro (DMA) granularity — 210 metros in one row, each with its Nielsen
code:

```json
{
    "surface": "geo",
    "resolution": "DMA",
    "keywords": ["coffee", "tea", "matcha"],
    "comparisonGroup": "coffee | tea | matcha",
    "regions": [
        {
            "geoCode": "759",
            "geoName": "Cheyenne WY-Scottsbluff NE",
            "values": [69, 28, 3],
            "formattedValues": ["69%", "28%", "3%"],
            "hasData": [true, true, true],
            "maxValueIndex": 0
        },
        {
            "geoCode": "744",
            "geoName": "Honolulu HI",
            "values": [65, 27, 8],
            "formattedValues": ["65%", "27%", "8%"],
            "hasData": [true, true, true],
            "maxValueIndex": 0
        }
    ]
}
```

**Related queries** for `matcha`, top and rising:

```json
{
    "surface": "relatedQueries",
    "keyword": "matcha",
    "geo": "US",
    "time": "today 12-m",
    "top": [
        { "rank": "TOP", "query": "matcha latte", "value": 100, "formattedValue": "100", "isBreakout": false },
        { "rank": "TOP", "query": "matcha tea", "value": 94, "formattedValue": "94", "isBreakout": false },
        { "rank": "TOP", "query": "matcha near me", "value": 73, "formattedValue": "73", "isBreakout": false }
    ],
    "rising": [
        { "rank": "RISING", "query": "grind rail", "value": 26950, "formattedValue": "Breakout", "isBreakout": true }
    ]
}
```

### Use it as a Google Trends API

There is no official public Google Trends API, and the maintained open-source libraries are gone —
`pytrends` was archived in April 2025 and `trendspy` has been dormant since December 2024. This
Google Trends scraper is the replacement.

Start a run with your input JSON, poll it, then read the dataset in JSON or CSV. Anything that can
call an HTTP endpoint can drive it:

- **Python** — `apify-client` or plain `requests` against the Apify API.
- **Node.js / TypeScript** — the official `apify-client` package.
- **cURL / any HTTP client** — start a run, fetch `/datasets/{id}/items?format=csv`.
- **Schedules** — have Apify run your keyword list nightly or weekly without writing a cron.
- **Webhooks** — fire your own endpoint the moment a run finishes.

#### Integrations: n8n, Make, Zapier and MCP

The Actor plugs into the Apify integrations you already have: **n8n**, **Make**, **Zapier**,
**Slack**, **Google Sheets**, **Google Drive**, **GitHub**, **Airtable** and **Zendesk**, plus any
warehouse you can point a webhook at. For AI agents, it is reachable over the **Apify MCP server**,
so an agent can ask for Google Trends data as a tool call.

Runs execute under **limited permissions** — the Actor needs no access to your account and collects
no personal data.

### How much does it cost to scrape Google Trends?

> **$0.004 per row — $4.00 per 1,000 rows.** One flat rate, the same on every plan, with **no start
> fee** and no tiers.

**Pay per row of data.** You are charged one `trends-row` event for every dataset row that carries
data, and nothing else. A row is a complete answer for one keyword on one surface: an
interest-over-time row for a one-year window holds 54 weekly buckets and the period averages — not
one bucket. Watch out for per-data-point pricing elsewhere, which makes a headline number look far
lower than it is.

**A run that returns nothing costs nothing.** Google answers `200 OK` with an empty payload more
often than you would think — obscure keywords, tiny regions, dead time windows. This Actor counts
parsed records, never HTTP status. An empty surface is written to your dataset as an explicit
`noData` row with a plain-English reason, and it is **not charged**. Failed requests are never
charged either. Set **Max total charge** on the run if you want a hard ceiling; the Actor stops
cleanly at the limit and keeps everything it has already collected.

**Is it free?** No, but the first test is nearly free: two keywords with interest over time only is
2 rows, so **$0.008**. If you are on the Apify free plan, a paid Actor draws on your monthly usage
allowance like anything else.

#### What a run costs in requests

Every request to Google is a chance to be rate-limited, so it is worth knowing where they go. A
"lookup" is the one call that fetches the tokens for a keyword or a comparison set; every surface for
that keyword reuses it.

| Surface                | Bulk mode (per keyword) | Comparison mode (per set of 2–5) |
| ---------------------- | ----------------------- | -------------------------------- |
| Lookup (always needed) | 1                       | 1 for the whole set              |
| Interest over time     | +1 → **1 row**          | +1 → **1 row for the whole set** |
| Interest by region     | +1 → **1 row**          | +1 → **1 row for the whole set** |
| Related queries        | +1 → **1 row**          | +1 per keyword → 1 row each      |

Comparison mode is the cheaper way to get a trend line for several keywords: one request and one row
covers the whole set. Bulk mode is the way to cover a long list.

### How this Google Trends scraper is built differently

Measured on the Apify platform, 9 August 2026:

| Run                                                       | Requests | Rows | Wall clock |
| --------------------------------------------------------- | -------- | ---- | ---------- |
| 2 keywords, interest over time, worldwide                 | 4        | 2    | 3.0 s      |
| 8 keywords, interest over time, US                        | 16       | 8    | 8.0 s      |
| 3 keywords compared, + region (DMA) + related queries, US | 7        | 5    | 21.4 s     |

- **No browser, so there is no DOM to break.** Trends scrapers built on a headless browser read
  Google's rendered page. When Google changes that markup — which it does — they return nothing, and
  they often keep charging while they do it. This Actor calls the JSON endpoints directly.
- **A real co-normalized comparison.** Up to 5 keywords in one request on one shared scale, not
  separate scrapes stitched together.
- **Per-surface opt-in.** Every surface you leave switched off is a request that cannot fail and a
  row you do not pay for. Fewer requests means faster, cheaper and markedly more reliable runs.
- **Charging tied to parsed rows, not HTTP status.** This is the difference between "the run
  finished" and "you got data".
- **100% of public runs have succeeded so far.** That is an honest but small sample — this Actor is
  new. The architecture is the argument; the track record is still being built.

### Reliability and fair use

- **Public data only.** Everything comes from the same public JSON endpoints that render the Google
  Trends website for any visitor. No logins, no accounts, no CAPTCHA solving, nothing behind a
  paywall, no personal data.
- **robots.txt is respected.** `trends.google.com/robots.txt` disallows exactly two paths, both forms
  of the `explore?` page. This Actor never fetches them. The `link` fields inside related-query
  results point at that disallowed page, so they are passed through to you as data and never
  followed.
- **Polite by design.** A batch is many small sequential lookups, not a burst. Retries back off
  (about 2s, 6s, 15s) and rotate to a fresh IP and cookie jar rather than hammering. Every run has a
  request budget and a wall clock.
- **Honest identification.** The Actor identifies itself in its user agent. No browser fingerprint
  spoofing, no bot-detection circumvention of any kind.
- **Limited permissions.** The Actor needs no access to your account and collects no personal data.

### Limitations worth knowing

- **Related topics currently returns nothing.** Google answers the related-topics surface with an
  empty list for every keyword we have tested, including keywords first resolved to Knowledge Graph
  entities — while related *queries* come back rich in the very same session and the very same
  lookup. This is on Google's side, not ours. The surface is off by default, warns loudly when you
  switch it on, reports `noData` instead of failing, and is never charged for. It will start working
  by itself if Google restores it. **Do not buy this Actor for related topics.**
- **This Actor does not scrape "Trending Now" / daily trending searches.** It is a keyword-explore
  tool: you bring the keywords. If you need the real-time trending feed, use a trending-searches
  Actor instead.
- **Comparison mode crushes small terms, by design.** A niche keyword next to a huge one lands in
  single digits, because that is what "relative to the biggest one" means. Use bulk mode if you want
  each keyword measured against itself.
- **Metro (DMA) granularity is a US concept.** It returns 210 metros for US locations and nothing
  outside the US — the Actor says so in the row's `noData` reason rather than failing.
- **City granularity returns fewer rows than you might expect.** Google only reports places with
  enough search volume to be statistically meaningful.
- **Google's numbers are a relative index, not search volumes.** 0–100 relative to the peak in your
  query. Nobody, including Google, publishes absolute Trends query counts.
- **Trends data is sampled**, so two runs of the same query can differ slightly. Pin the time zone
  offset to `0` if you want runs to line up as closely as possible.
- **The trailing bucket is usually incomplete.** It is flagged `isPartial: true` and kept rather than
  silently dropped, because whether to use it is your call.

### FAQ

#### Is scraping Google Trends legal?

This Actor reads the same public JSON endpoints that render the Google Trends website for any
visitor. It uses no login, solves no CAPTCHA, circumvents no bot detection, collects no personal
data, and respects `trends.google.com/robots.txt`. You are responsible for how you use the data,
including any licensing or attribution your own use requires.

#### Why do my numbers change between runs?

Google Trends is built on a sample of searches, so repeated queries can differ slightly. Pinning the
time zone offset to `0` makes runs line up as closely as possible.

#### Why is my keyword showing 0 or a single-digit value?

Almost always comparison mode. Next to a much larger term, a niche keyword is genuinely that small
*relative to it*. Switch comparison off to measure each keyword against its own peak.

#### Can I get search volume numbers?

No — and neither can anyone else. Google publishes a relative 0–100 index only. Pair this with a
keyword volume tool if you need absolute numbers.

#### How many keywords can I scrape at once?

Unlimited in bulk mode. Comparison mode is capped at 5, which is Google's own limit.

#### Can I export to CSV or Excel?

Yes — the dataset exports as JSON, CSV, Excel, XML or HTML, or you can pull it from the Apify API.

#### Does it support custom date ranges?

Yes: presets, a `2024-01-01 2024-12-31` date range, or an hourly `2024-01-01T00 2024-01-07T23`
window. Invalid ranges are rejected before any request is made.

#### Will I be charged if Google returns nothing?

No. Billing counts parsed rows, not requests or HTTP status codes. Empty surfaces are written as
uncharged `noData` rows, and failed requests are never charged.

### Other Actors by this developer

- **[Career Site Job Listings Scraper](https://apify.com/nimait/career-site-job-listings-scraper)** —
  turn any Greenhouse, Lever, Ashby or Workday careers URL into clean job data, every posting in one
  identical shape. Pay per job returned.

### Support

Found a query that behaves oddly, or want a surface added? Open an issue on the Actor page with the
exact input JSON — that is the fastest way to get it looked at.

***

#### For developers

```bash
npm install
npm run build     # tsc
npm run lint
npm test          # vitest, no network
npx apify validate-schema
npm run start:dev # local run, reads storage/key_value_stores/default/INPUT.json
```

Source layout: `src/input.ts` (parse and validate into a `RunConfig`, pure), `src/run.ts` (the run
loop, network injected), `src/charging.ts` (the one function that decides whether a row is
billable), `src/trends/*` (a standalone Trends client: session and cookie handling, the explore
token dance, XSSI stripping, typed widget parsers, the retry and budget envelope).
`../DEVLOG_TRENDS.md` carries the full decision trail and every live measurement quoted above.

# Actor input Schema

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

One search term per line. There is no limit on how many keywords you can scrape in a single run when "Compare keywords" is off — each one is looked up independently, one after another. Comparison mode is capped at 5 keywords, which is Google's own limit.

## `compare` (type: `boolean`):

Off (default): every keyword is scraped independently, so each one gets its own 0-100 scale where 100 is that keyword's own peak. This is what you want for scraping a keyword list. On: all keywords go into one comparison, which puts them on a single shared scale — the peak of the most searched term becomes 100 and everything else is measured against it. That is the only way the numbers are comparable between terms, and it is why a niche keyword next to a huge one often sits in single digits. Comparison mode needs 2 to 5 keywords.

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

The trend line: one value per time bucket for every keyword, plus the period average per keyword. This is the surface most people want.

## `includeGeo` (type: `boolean`):

Where the searches come from, broken down by subregion of your chosen location (or by country when the location is worldwide). Granularity is set by "Region granularity" below. Adds one request per keyword.

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

The searches people also ran, split into top (most common over the period) and rising (fastest growing, where "Breakout" means growth above 5000%). Adds one request per keyword.

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

The subjects people also searched for, as Google Knowledge Graph topics rather than raw text. Be warned: Google currently returns nothing for this surface on every keyword we have tested, including keywords resolved to topic entities first. It is left in because it costs you nothing when it is empty — no-data results are never charged for — and it will start working by itself if Google restores the surface. Adds up to three requests per keyword.

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

Where to measure search interest. Leave empty for worldwide. Otherwise use a two-letter country code (US, GB, DE, AU) or a country subregion (US-NY, GB-ENG).

## `time` (type: `string`):

A preset — 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 your own range as two dates, "2024-01-01 2024-12-31", or two hours, "2024-01-01T00 2024-01-07T23". The bucket size follows the range: hourly for the "now" presets and hourly ranges, daily up to about 9 months, weekly for a year, monthly for 5 years and longer. Anything Google cannot read is rejected before the run makes a single request.

## `searchType` (type: `string`):

Which Google search surface to measure interest on. Web search is the default and the one all the familiar Trends numbers come from. The others measure the same terms inside Google Images, Google News, Google Shopping and YouTube search — useful when a term behaves differently on video or retail than it does on the web.

## `geoResolution` (type: `string`):

How finely to break down "Interest by region". Automatic follows Google's own choice for your location: countries when the location is worldwide, subregions when it is a country. Metro (DMA) is a United States concept and only returns data for US locations. City is the finest available and returns fewer rows, because Google only reports places with enough search volume to be meaningful.

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

Google rate-limits Trends requests by IP. Datacenter proxy is the default: it is included in every Apify plan and measured on the platform it was three times faster than residential while returning identical data. Rate limiting does happen occasionally, and the Actor answers it by rotating to a fresh IP and cookie jar and retrying. Switch to residential if you are running very large batches and want the widest IP pool. Running with no proxy at all works too for occasional small runs.

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

Restricts the search to one Google Trends category, which is the cleanest way to separate meanings of an ambiguous word — "jaguar" in Autos & Vehicles (id 47) is a very different trend from "jaguar" in Pets & Animals (id 66). Leave at 0 for all categories. Category ids are the ones in the Trends web interface URL (the cat= parameter).

## `resolveEntities` (type: `boolean`):

Only affects the related-topics surface. When on, each keyword is first matched to a Google topic entity (for example "coffee" to the topic Coffee, a Beverage) and the topic lookup uses that entity. Turn it off to query the literal text instead.

## `hl` (type: `string`):

Language Google formats the results in. It changes the wording of the human-readable date labels and topic names, not the numbers.

## `tz` (type: `integer`):

Minutes from UTC, used to decide where time buckets start and end. 0 (UTC) keeps results reproducible between runs, which is usually what you want for a dataset. Use -300 for US Eastern, 60 for Central European, and so on.

## `maxRequestRetries` (type: `integer`):

How many times a request to Google is retried before that surface is reported as failed. Retries wait about 2, 6 and 15 seconds, and the Actor switches to a fresh IP and cookie jar from the second retry onwards.

## Actor input object example

```json
{
  "keywords": [
    "coffee",
    "tea"
  ],
  "compare": false,
  "includeTimeseries": true,
  "includeGeo": false,
  "includeRelatedQueries": false,
  "includeRelatedTopics": false,
  "geo": "",
  "time": "today 12-m",
  "searchType": "web",
  "geoResolution": "auto",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "category": 0,
  "resolveEntities": true,
  "hl": "en-US",
  "tz": 0,
  "maxRequestRetries": 3
}
```

# Actor output Schema

## `rows` (type: `string`):

Every surface you asked for, one row each: interest over time with per-keyword averages, interest by region, related queries and related topics.

## `rowsCsv` (type: `string`):

The same rows as a spreadsheet-ready CSV file.

## `dataset` (type: `string`):

Browse, filter and export the results in Apify Console.

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

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

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

```

## MCP server setup

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