# Google Trends Intelligence (`skootle/google-trends-intelligence`) Actor

Google Trends interest over time and related queries with trend direction, momentum, volatility, source proof, API output, scheduling, monitoring, and integrations.

- **URL**: https://apify.com/skootle/google-trends-intelligence.md
- **Developed by:** [Skootle](https://apify.com/skootle) (community)
- **Categories:** SEO tools, AI, Automation
- **Stats:** 1 total users, 0 monthly users, 66.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 google trends intelligence records

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/platform/actors/running/actors-in-store#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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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

<!-- skootle:growth-loop-hook -->
## Google Trends Intelligence

<!-- skootle:start -->
Built by [Skootle](https://skootle.com) for reliable, decision-ready data.
<!-- skootle:end -->

![Google Trends Intelligence hero](https://raw.githubusercontent.com/kesjam/skootle-actors-assets/main/heroes/google-trends-intelligence.png)

**TL;DR**

- Pulls Google Trends interest over time and related queries for up to 5 keywords in one run, then ships three record types: raw `TREND_POINT` rows, ranked `RELATED_QUERY` rows, and a per-keyword `SUMMARY` with the trend maths already done.
- Direction, volatility, momentum, peak and trough are computed for you. Direction comes from an ordinary least squares fit across the whole window, not from comparing the first and last reading.
- A block, a rate limit, or a captcha interstitial **fails the run loudly** with the HTTP status and a body snippet. It never returns as an empty dataset.
- A genuine no-match is marked explicitly with `zeroMatch: true` and `sourceStatus: "ok"`, so you can tell "Google has no data" apart from "the scraper got shut out".
- Built for people who feed trend data into decisions: SEO and content teams sizing demand, brand and product teams tracking interest curves, and agents that need clean numeric fields plus a prewritten `agentMarkdown` summary.

<!-- skootle:review-cta -->
If this Actor saves you analysis time, share what worked on the [reviews page](https://apify.com/skootle/google-trends-intelligence/reviews). Report bugs through Issues so they can be fixed with source evidence.

---

### What does this actor return?

Every run writes one dataset. Filter on `recordType` to split the three shapes. Every record carries `recordId`, `schemaVersion`, `sourceName`, `sourceQueryUrl`, `trendsUrl`, `retrievedAt`, `fieldCompletenessScore` and `agentMarkdown`.

- `trendsUrl` is the human Google Trends chart for that exact query, so any number can be opened at source.
- `sourceQueryUrl` is the API URL the row came from.
- `fieldCompletenessScore` is the percentage of scored fields populated, so you can filter thin rows without inspecting each one.
- `agentMarkdown` is a short prewritten summary of the row, for piping records into an LLM instead of a spreadsheet.

#### TREND_POINT

One row per keyword per time bucket. The rolling average and the change against the previous bucket are precomputed.

```json
{
  "recordType": "TREND_POINT",
  "recordId": "gtrends:point:cold-plunge:US:2026-05-18:1747526400",
  "schemaVersion": "1.0.0",
  "queryLabel": "cold plunge vs sauna | US | today 3-m",
  "keyword": "cold plunge",
  "keywordIndex": 0,
  "geo": "US",
  "geoLabel": "US",
  "timeframe": "today 3-m",
  "category": 0,
  "property": "",
  "propertyLabel": "web_search",
  "dateIso": "2026-05-18",
  "timestampSeconds": 1747526400,
  "timestampIso": "2026-05-18T00:00:00.000Z",
  "formattedTime": "May 18, 2026",
  "bucketIndex": 47,
  "interestValue": 68,
  "interestFormatted": "68",
  "hasData": true,
  "isPartialBucket": false,
  "changeFromPreviousPct": 9.7,
  "changeFromPreviousAbs": 6,
  "rollingAverage7": 63.14,
  "fieldCompletenessScore": 100,
  "sourceName": "Google Trends public web endpoints",
  "sourceQueryUrl": "https://trends.google.com/trends/api/widgetdata/multiline?hl=en-US&tz=0&req=%7B%22time%22%3A%222026-02-18%202026-05-18%22%7D&token=APP6_UEAAAAA...",
  "trendsUrl": "https://trends.google.com/trends/explore?q=cold+plunge&date=today+3-m&geo=US",
  "retrievedAt": "2026-05-19T14:02:11.884Z",
  "agentMarkdown": "## cold plunge on 2026-05-18: interest 68/100\n- Window: today 3-m in US\n- 7 bucket rolling average: 63.14\n- Change vs previous bucket: 9.7%\n- Bucket is complete.\n- Source: https://trends.google.com/trends/explore?q=cold+plunge&date=today+3-m&geo=US"
}
````

#### RELATED\_QUERY

One row per related query, from either the `top` list or the `rising` list. Rising rows carry both the label Google printed and a parsed numeric growth, plus an explicit `isBreakout` boolean.

```json
{
  "recordType": "RELATED_QUERY",
  "recordId": "gtrends:related:cold-plunge:rising:3:cold-plunge-tub-for-sale",
  "schemaVersion": "1.0.0",
  "queryLabel": "cold plunge vs sauna | US | today 3-m",
  "keyword": "cold plunge",
  "relatedQuery": "cold plunge tub for sale",
  "listType": "rising",
  "rankPosition": 3,
  "relativeValue": 1350,
  "formattedValue": "+1,350%",
  "growthPct": 1350,
  "isBreakout": false,
  "hasData": true,
  "geo": "US",
  "geoLabel": "US",
  "timeframe": "today 3-m",
  "category": 0,
  "property": "",
  "propertyLabel": "web_search",
  "relatedQueryUrl": "https://trends.google.com/trends/explore?q=cold+plunge+tub+for+sale&date=today+3-m&geo=US",
  "fieldCompletenessScore": 100,
  "sourceName": "Google Trends public web endpoints",
  "sourceQueryUrl": "https://trends.google.com/trends/api/widgetdata/relatedsearches?hl=en-US&tz=0&req=%7B%22restriction%22%3A%7B%7D%7D&token=APP6_UEAAAAA...",
  "trendsUrl": "https://trends.google.com/trends/explore?q=cold+plunge&date=today+3-m&geo=US",
  "retrievedAt": "2026-05-19T14:02:13.201Z",
  "agentMarkdown": "## cold plunge tub for sale (rising related query for \"cold plunge\")\n- Rank 3 in the rising list\n- Strength: growth 1350% over the window\n- Window: today 3-m in US\n- Open in Trends: https://trends.google.com/trends/explore?q=cold+plunge+tub+for+sale&date=today+3-m&geo=US"
}
```

For `listType: "top"`, `relativeValue` is a 0 to 100 index against the strongest related query, and `growthPct` is `null`. For a query Google labels `Breakout`, `formattedValue` is `"Breakout"`, `isBreakout` is `true`, and `growthPct` is `null` because Google does not publish an exact figure above 5000 percent.

#### SUMMARY

One row per keyword per run. This is the record most buyers actually consume.

```json
{
  "recordType": "SUMMARY",
  "recordId": "gtrends:summary:cold-plunge:US:today-3-m",
  "schemaVersion": "1.0.0",
  "headline": "cold plunge: rising, peak 100, average 61.42, 2 breakout queries",
  "queryLabel": "cold plunge vs sauna | US | today 3-m",
  "keyword": "cold plunge",
  "keywordIndex": 0,
  "comparedAgainst": ["sauna"],
  "geo": "US",
  "geoLabel": "US",
  "timeframe": "today 3-m",
  "category": 0,
  "property": "",
  "propertyLabel": "web_search",
  "observationCount": 89,
  "partialBucketsExcluded": 1,
  "windowStartDate": "2026-02-18",
  "windowEndDate": "2026-05-19",
  "peakInterest": 100,
  "peakDate": "2026-05-11",
  "troughInterest": 31,
  "troughDate": "2026-02-24",
  "averageInterest": 61.42,
  "googleReportedAverage": 61,
  "medianInterest": 60,
  "latestInterest": 68,
  "standardDeviation": 14.87,
  "coefficientOfVariation": 0.2421,
  "volatility": "moderate",
  "slopePerBucket": 0.2913,
  "slopeRSquared": 0.6104,
  "totalChangeOverWindow": 25.63,
  "direction": "rising",
  "momentumPct": 34.8,
  "dataCoveragePct": 100,
  "topQueryCount": 25,
  "risingQueryCount": 25,
  "breakoutQueryCount": 2,
  "topRisingQueries": [
    { "query": "cold plunge chiller", "formattedValue": "Breakout", "growthPct": null, "isBreakout": true },
    { "query": "portable cold plunge", "formattedValue": "Breakout", "growthPct": null, "isBreakout": true },
    { "query": "cold plunge tub for sale", "formattedValue": "+1,350%", "growthPct": 1350, "isBreakout": false },
    { "query": "cold plunge benefits", "formattedValue": "+480%", "growthPct": 480, "isBreakout": false },
    { "query": "diy cold plunge", "formattedValue": "+210%", "growthPct": 210, "isBreakout": false }
  ],
  "topQueries": [
    { "query": "cold plunge tub", "relativeValue": 100 },
    { "query": "cold plunge benefits", "relativeValue": 74 },
    { "query": "ice bath", "relativeValue": 52 },
    { "query": "cold plunge near me", "relativeValue": 41 },
    { "query": "best cold plunge", "relativeValue": 33 }
  ],
  "breakoutQueries": ["cold plunge chiller", "portable cold plunge"],
  "sourceStatus": "ok",
  "zeroMatch": false,
  "interestOverTimeRequested": true,
  "relatedQueriesRequested": true,
  "interestOverTimeReturned": true,
  "relatedQueriesReturned": true,
  "notes": [],
  "methodology": [
    "Interest values are Google index numbers from 0 to 100 relative to the peak of this exact query, not search counts.",
    "Averages, median, slope and volatility exclude any bucket Google flagged as still filling."
  ],
  "fieldCompletenessScore": 100,
  "sourceName": "Google Trends public web endpoints",
  "sourceQueryUrl": "https://trends.google.com/trends/api/explore?hl=en-US&tz=0&req=%7B%22comparisonItem%22%3A%5B%5D%7D",
  "trendsUrl": "https://trends.google.com/trends/explore?q=cold+plunge&date=today+3-m&geo=US",
  "retrievedAt": "2026-05-19T14:02:13.204Z",
  "agentMarkdown": "## cold plunge: rising, peak 100, average 61.42, 2 breakout queries\n- \"cold plunge\" is rising: the fitted line gains 25.63 interest points across today 3-m.\n- Peak 100 on 2026-05-11, trough 31 on 2026-02-24.\n- Average 61.42, median 60, latest 68 across 89 complete buckets.\n- Volatility moderate (coefficient of variation 0.2421), momentum 34.8%.\n- Related demand: 25 top queries, 25 rising, 2 breakout.\n- Fastest risers: cold plunge chiller (Breakout), portable cold plunge (Breakout), cold plunge tub for sale (+1,350%), cold plunge benefits (+480%), diy cold plunge (+210%).\n- Source status: ok.\n- Source: https://trends.google.com/trends/explore?q=cold+plunge&date=today+3-m&geo=US"
}
```

Every `SUMMARY` row ships six `methodology` notes. Two are shown here for space; all six are in the Methodology section below.

***

### How is this different from other Google Trends scrapers?

**1. A block is never delivered as an empty result.**

This is the main one. Most Trends scrapers classify anything that is not parseable JSON as "no data" and hand you an empty array with a 200-flavoured success. You then write a report saying interest collapsed, when in fact a residential exit node got burned and Google served a captcha page.

This actor classifies every HTTP response into one of four outcomes before parsing:

| Class | Trigger | Action |
|---|---|---|
| `ok` | Body looks like a Trends payload | Parse it |
| `retry` | HTTP 429, any 5xx, an empty 200, or a socket failure | Back off, rotate proxy session, try again |
| `blocked` | HTTP 403, or a 200 carrying an HTML interstitial, a `/sorry/index` redirect, a reCAPTCHA, or an "unusual traffic" notice | Rotate and retry, then fail the run with the status and a body snippet |
| `fatal` | A 4xx other than 403 and 429, or a body that is not JSON after the XSSI guard is stripped | Fail immediately, because retrying a malformed request cannot help |

A JSON endpoint that answers with `<!doctype html` is an interstitial, not data, so it is treated as a block and can never be mistaken for an empty result.

**2. Trend maths that survives a noisy day.**

Direction is an OLS slope across every bucket in the window. A single spike or a single dead day cannot flip the label. You also get `slopeRSquared` so you can tell a clean trend from a scattered one before you act on it.

**3. Partial buckets are excluded from the maths.**

Google flags the trailing bucket while it is still filling. Include it and every average and slope drags toward a decline that has not happened. Those buckets are still emitted as `TREND_POINT` rows with `isPartialBucket: true`, so nothing is hidden, but the `SUMMARY` excludes them and reports how many it excluded in `partialBucketsExcluded`.

**4. Breakout is a boolean, not a magic number.**

Google prints `Breakout` and sends a large sentinel value alongside it. This actor derives `isBreakout` from the label Google actually printed rather than by threshold-guessing the sentinel, and sets `growthPct` to `null` rather than reporting a fake precise number.

**5. Every row is traceable.**

`trendsUrl` opens the exact chart. `sourceQueryUrl` is the API call. `retrievedAt` is the timestamp. If a number looks wrong you can check it in about ten seconds.

***

### How do I know the numbers are real?

![Real output: US interest over time for artificial intelligence and machine learning, with the SUMMARY fields printed underneath](https://raw.githubusercontent.com/kesjam/skootle-actors-assets/main/heroes/google-trends-intelligence-proof.png)

Drawn from run `Tw3XwwlVBKyWPVOuQ`, 258 records, not a mockup. Every label under it is a field you can filter on. Here is how to check any run yourself.

- **Open the chart.** Every record carries `trendsUrl` pointing at the human Google Trends page for that exact keyword, geo, timeframe, category and property. The page should match the record.
- **Cross-check the average.** `SUMMARY` reports both `averageInterest` (computed here, partial buckets excluded) and `googleReportedAverage`. They should be close. A large gap means partial or missing buckets, which `partialBucketsExcluded` and `dataCoveragePct` confirm.
- **Check the run posture fields.** `sourceStatus`, `zeroMatch`, `interestOverTimeReturned`, `relatedQueriesReturned` and `notes` describe what happened upstream. Any problem shows up there, and a blocked call fails the run instead of writing a record.
- **Check coverage.** `dataCoveragePct` is the share of buckets Google reported real data for. Low-volume keywords give sparse series, and this makes that visible instead of silently averaging zeros.

***

### What input does it take?

| Field | Type | Default | Description |
|---|---|---|---|
| `keywords` | array of strings | required | 1 to 5 keywords. Each is 1 to 100 characters. More than one keyword makes it a Google Trends comparison, and the 0 to 100 index is then relative across the whole set. |
| `geo` | string | `"US"` | Google geo code, up to 10 characters. `"US"`, `"GB"`, `"US-CA"`, `"US-NY-501"`. Empty string means worldwide. |
| `timeframe` | enum | `"today 3-m"` | Window to pull. See the valid values below. |
| `category` | integer | `0` | Google Trends category id, 0 to 1500. `0` is all categories. |
| `property` | enum | `""` | Search surface. See the valid values below. |
| `maxRelatedQueries` | integer | `25` | 0 to 50. Caps how many rows are emitted from each of the top and rising lists, per keyword. |
| `includeInterestOverTime` | boolean | `true` | Set to `false` to skip the timeline call and emit only related queries plus the summary. |
| `includeRelatedQueries` | boolean | `true` | Set to `false` to skip the related-queries calls and emit only the timeline plus the summary. |
| `language` | string | `"en-US"` | Passed to Google as `hl`. 2 to 10 characters. Affects the language of formatted labels. |
| `proxyCountryCode` | string | `"US"` | Two-letter country code for the proxy exit node. Match it to `geo` where you can, since Trends responses vary by request origin. |
| `maxRetriesPerRequest` | integer | `4` | 1 to 8. Attempts per HTTP call before the run fails. Each retry gets exponential backoff with jitter and a fresh proxy session. |

#### Valid `timeframe` values

| Value | Window |
|---|---|
| `now 1-H` | Past hour |
| `now 4-H` | Past 4 hours |
| `now 1-d` | Past day |
| `now 7-d` | Past 7 days |
| `today 1-m` | Past 30 days |
| `today 3-m` | Past 90 days |
| `today 12-m` | Past 12 months |
| `today 5-y` | Past 5 years |
| `all` | 2004 to present |

Google picks the bucket size from the window. Short `now` windows return sub-hourly or hourly buckets, `today 1-m` and `today 3-m` return daily buckets, `today 12-m` and `today 5-y` return weekly buckets, and `all` returns monthly buckets. That choice is Google's, not this actor's, and it is why row counts vary so much by timeframe.

#### Valid `property` values

| Value | Surface |
|---|---|
| `""` | Web search (the default, and what most buyers want) |
| `images` | Google Images |
| `news` | Google News |
| `froogle` | Google Shopping |
| `youtube` | YouTube search |

#### Example input

```json
{
  "keywords": ["cold plunge", "sauna"],
  "geo": "US",
  "timeframe": "today 3-m",
  "category": 0,
  "property": "",
  "maxRelatedQueries": 25,
  "includeInterestOverTime": true,
  "includeRelatedQueries": true,
  "language": "en-US",
  "proxyCountryCode": "US",
  "maxRetriesPerRequest": 4
}
```

***

### How much does a run cost?

Cost tracks the number of upstream HTTP calls and the number of dataset rows, so here are the drivers rather than a made-up figure.

**HTTP calls per run:**

1. One landing-page call to pick up the consent and NID cookies. A failure here is a warning, not a stop.
2. One explore call. This covers all of your keywords at once and returns the short-lived widget tokens.
3. One interest-over-time call. Google's timeline widget covers every comparison item in a single response, so this does not multiply by keyword. Skipped if `includeInterestOverTime` is `false`.
4. One related-queries call per keyword. This is the part that scales with `keywords.length`. Skipped if `includeRelatedQueries` is `false`.

So a 1-keyword run is about 4 calls and a 5-keyword run is about 8 calls. Comparing 5 keywords in one run is materially cheaper than 5 separate single-keyword runs, because the landing page, explore and timeline calls are shared.

**What increases cost:**

- **Longer timeframes and finer buckets.** `TREND_POINT` rows equal buckets multiplied by keywords. A 5-year weekly window and a 90-day daily window produce broadly similar row counts, while `now 1-d` at sub-hourly resolution produces far more.
- **`maxRelatedQueries`.** Up to `maxRelatedQueries` rows from the top list plus up to `maxRelatedQueries` from the rising list, per keyword.
- **Retries.** Each retried attempt is another proxied request. Blocked and rate-limited attempts consume proxy traffic before the run fails.
- **Proxy traffic.** Residential proxy egress is usually the larger line item, not compute. Trends payloads are small JSON documents, so this stays modest.

**What reduces cost:**

- Set `includeRelatedQueries: false` if you only want the interest curve, or `includeInterestOverTime: false` if you only want the query lists.
- Lower `maxRelatedQueries`.
- Batch related keywords into one run rather than firing one run per keyword.

***

### What are the limits?

- **5 keywords per run.** This is Google's comparison limit, not an artificial cap.
- **Interest values are relative, not absolute.** There is no way to get search counts out of Google Trends. See Methodology below.
- **Comparison changes the numbers.** In a multi-keyword run the 0 to 100 index is scaled across the whole set, so the same keyword run alone and run against a bigger keyword will report different values. Both are correct. They answer different questions.
- **No geographic breakdown or topic data yet.** This actor covers interest over time and related queries. Interest by region and related topics are not in v0.1.0.
- **Low-volume keywords return sparse or empty series.** Google withholds data below a volume floor. That surfaces as `zeroMatch: true` with `sourceStatus: "ok"`, or as a low `dataCoveragePct`, not as an error.
- **Trends data is not real time.** Even the shortest windows lag, and the trailing bucket is flagged partial until it fills.
- **Google can change the protocol.** The three-call flow, the XSSI guard, and the widget naming are all undocumented surfaces. When they drift, this actor throws a `fatal` error naming the exact call that changed rather than returning a plausible-looking empty payload.
- **Rerunning the same query can shift values slightly.** Google samples. Small movements between runs are normal and are not a bug in this actor.

***

### Methodology

**Interest values are a 0 to 100 relative index, not search counts.** Google scales the series so that the highest point in the requested window equals 100 and everything else is a proportion of that. Change the timeframe, the geo, the category, the property, or the set of compared keywords, and every number in the series can change. A value of 50 means "half the interest of this query's own peak in this exact window". It does not mean 50 searches, 50 thousand searches, or 50 percent of anything else.

**Partial buckets are excluded from the maths.** Google flags the trailing bucket with `isPartial` while it is still filling. Those buckets are emitted as `TREND_POINT` rows so you can see them, but the `SUMMARY` computes average, median, standard deviation, slope, direction and volatility without them, and reports the count in `partialBucketsExcluded`. Including a half-collected day is the single most common way a Trends report invents a collapse that never happened.

**Direction is an OLS slope, not an endpoint comparison.** An ordinary least squares regression is fitted across every complete bucket, indexed by position. The reported `slopePerBucket` is interest points gained per bucket, and `totalChangeOverWindow` is that slope extended across the window. The label is assigned relative to the series mean, not in absolute points, because 0.2 points per day means something very different for a keyword averaging 5 than for one averaging 80:

- `rising` when the fitted change over the window is at least 10 percent of the series mean
- `falling` when it is at most negative 10 percent of the series mean
- `flat` when it falls between those
- `unknown` when there are fewer than 3 complete buckets

`slopeRSquared` reports how much of the variance the fitted line explains, so you can weigh the direction label by how well it actually describes the series.

**Volatility is the coefficient of variation, banded.** Standard deviation divided by the mean, reported raw as `coefficientOfVariation` and banded as `volatility`:

- `low` below 0.15
- `moderate` from 0.15 up to 0.35
- `high` at 0.35 and above
- `unknown` when the mean is zero or there are too few points

High volatility means the keyword spikes on events rather than holding a level, which changes whether you buy against it or wait for the next spike.

**Momentum compares the recent quarter to the earliest quarter.** `momentumPct` is the mean of the last quarter of the window against the mean of the first quarter, as a percentage change. It needs at least 4 complete buckets. This catches the case the slope misses: a keyword that fell for two months and turned around last week has a negative slope and a positive momentum, and both readings are useful.

**Breakout means growth above 5000 percent.** Google prints the literal string `Breakout` on a rising query when growth exceeds 5000 percent, and does not publish a figure. Those rows get `isBreakout: true` and `growthPct: null`. Every other rising query gets its percentage parsed out of the printed label into `growthPct`, with the original label preserved in `formattedValue`.

***

### Silent zeros, and why this actor refuses to produce them

Worth stating plainly, because it is the design decision everything else follows from.

There are two very different reasons a Google Trends query comes back with nothing:

1. **Google answered normally and there is genuinely no data.** The keyword is below the volume floor, or the geo is too narrow, or the window is too short. This is a real answer.
2. **Google refused to answer.** The proxy exit node is burned, a rate limit hit, a captcha interstitial came back, or the protocol drifted. This is not an answer at all.

Most scrapers flatten both into the same empty array. That is what makes them dangerous in a pipeline. A silent zero looks exactly like a real zero, so it propagates into a dashboard, a report, or an agent's reasoning, and nobody finds out until someone makes a bad decision on it.

This actor separates them:

- **Case 1** produces a `SUMMARY` record with `zeroMatch: true` and `sourceStatus: "ok"`. The `agentMarkdown` says so in plain language: Google answered normally and reported no data, this is a real answer and not a failure, try a broader timeframe or a wider geo.
- **Case 2** fails the run. The error names the HTTP status, the URL, the number of attempts, and includes a body snippet so you can see what Google actually served. Nothing is written to the dataset pretending to be data.

A failed run is cheap to notice and cheap to rerun. A silently zeroed run is neither.

***

### FAQ

**Can I get actual search volumes?**
No, and neither can anyone else from Google Trends. Trends publishes a relative index only. If you need absolute volumes you need Keyword Planner or a third-party volume dataset, and then Trends is the right tool for shape and timing rather than magnitude.

**Why do the numbers change when I add a second keyword?**
Because the index rescales across the comparison set. With one keyword, 100 is that keyword's own peak. With two, 100 is the peak of whichever keyword is larger, and the smaller one gets squashed proportionally. Run keywords alone for individual shape, run them together for relative size.

**Why is my last data point lower than the rest?**
It is probably a partial bucket that is still filling. Check `isPartialBucket` on that `TREND_POINT` row. The `SUMMARY` already excludes it from the trend maths for exactly this reason.

**What happens if Google blocks the run?**
The run fails with a `TrendsSourceError` naming the HTTP status, the URL, the attempt count and a snippet of the body Google served. It does not write an empty dataset. Retrying later, or with a `proxyCountryCode` matched to your `geo`, usually clears it.

**`direction` says rising but the chart looks flat to me. Which is right?**
Check `slopeRSquared` and `totalChangeOverWindow`. A rising label with a low R squared means the fitted line does drift upward, but the series is scattered enough that the line is a poor description of it. The threshold for `rising` is a fitted change of 10 percent of the series mean, which can be a small absolute movement on a high-average keyword.

**Can I track a topic rather than a search term?**
Not in v0.1.0. Keywords are passed to Google as literal search terms. Trends entity and topic ids are not supported yet.

**Does `geo` accept sub-regions?**
Yes. Google's geo codes nest, so `US`, `US-CA` and `US-NY-501` (a metro code) all work. An empty string means worldwide. Narrower geos hit the data floor sooner, so expect more `zeroMatch` results at metro level.

**How do I use this with an LLM or agent?**
Every record carries an `agentMarkdown` field: a short prewritten summary with the numbers, the caveats and the source link already in prose. Feed that in directly rather than asking the model to interpret raw fields. The numeric fields are still there when you want the model to compute something.

***

### Tutorial: build a weekly demand brief

1. Start with two to five terms that answer one commercial question, such as `cold plunge`, `sauna`, and `ice bath`.
2. Use `today 3-m` for a campaign-planning view, `today 12-m` for seasonality, or `now 7-d` for a fast-moving launch.
3. Keep `includeInterestOverTime` and `includeRelatedQueries` enabled. Set `maxRelatedQueries` to 10 for a compact brief or 25 for fuller research.
4. Schedule the Actor weekly in Apify, then send the dataset to Sheets, a webhook, Make, Zapier, or your own API consumer.
5. Filter `recordType: "SUMMARY"` for one decision row per keyword. Use `direction`, `momentumPct`, `volatility`, `breakoutQueries`, and `topRisingQueries` to decide what deserves action.
6. Keep `TREND_POINT` rows for charts and `RELATED_QUERY` rows for content briefs, product language, and campaign expansion.

A useful operating rule is to treat `direction` as the long-window signal and `momentumPct` as the recent change. When they disagree, inspect `slopeRSquared`, the peak date, and the source chart before acting.

### Pricing

Google Trends Intelligence uses pay-per-result pricing with a small Actor start fee. Dataset result charges apply to every emitted `TREND_POINT`, `RELATED_QUERY`, and `SUMMARY` row. The live pricing card in the Apify Store is authoritative.

The lowest buyer price is set from measured Cloud economics, including compute, residential proxy traffic, retries, storage, failed-run exposure, and Apify's creator share. Batching up to five related keywords is usually cheaper than running each keyword separately because the landing, explore, and timeline calls are shared.

Every run has a $5 maximum total charge. Usage passthrough is disabled, so buyers see the Store price instead of receiving separate platform-resource charges.

### Legal and source terms

This Actor reads public Google Trends web endpoints and is not affiliated with, endorsed by, or sponsored by Google. Google Trends values are sampled relative indexes, not absolute search volumes. You are responsible for using the data in accordance with applicable law, Google's terms, and your own compliance requirements.

Do not use this Actor to infer sensitive traits about individuals, to evade access controls, or to make high-impact decisions without independent verification. Source URLs and retrieval timestamps are included so material findings can be checked against the human Google Trends interface.

### More from Skootle

Pair this Actor with other Skootle data products when a workflow needs both demand and execution evidence:

- [Website SEO Audit Intelligence](https://apify.com/skootle/website-seo-audit-intelligence) turns a demand hypothesis into a bounded technical and content audit.
- [Website Tech Stack Intelligence](https://apify.com/skootle/website-tech-stack-intelligence) adds explainable technology fingerprints, security headers, and integration signals for lead qualification.
- [Federal Contract Intelligence](https://apify.com/skootle/usaspending-contract-intelligence) connects public-sector demand research with award, agency, NAICS, and recompete evidence.

For support, open an Issue on the Actor page with the run ID, input, and the specific field that looks wrong. Keep private data and credentials out of issue reports. Reviews belong on the reviews page, while reproducible defects belong in Issues so they can be fixed against source evidence.

***

# Actor input Schema

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

One to five search terms. Multiple terms are normalized against the same comparison scale.

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

Google Trends geo code, such as US, GB, US-CA, or an empty string for worldwide.

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

Google Trends time window.

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

Google Trends category ID. Use 0 for all categories.

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

Search surface. An empty value means web search.

## `maxRelatedQueries` (type: `integer`):

Maximum rows from each of the top and rising lists, per keyword.

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

Return normalized timeline points and compute time-series metrics.

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

Return top and rising related searches for each keyword.

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

Google Trends interface locale.

## `proxyCountryCode` (type: `string`):

Two-letter country code for the residential proxy exit.

## `maxRetriesPerRequest` (type: `integer`):

Bounded retry count for transient and blocked responses.

## Actor input object example

```json
{
  "keywords": [
    "artificial intelligence",
    "machine learning"
  ],
  "geo": "US",
  "timeframe": "today 3-m",
  "category": 0,
  "property": "",
  "maxRelatedQueries": 25,
  "includeInterestOverTime": true,
  "includeRelatedQueries": true,
  "language": "en-US",
  "proxyCountryCode": "US",
  "maxRetriesPerRequest": 4
}
```

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("skootle/google-trends-intelligence").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("skootle/google-trends-intelligence").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call skootle/google-trends-intelligence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=skootle/google-trends-intelligence",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Google Trends Intelligence",
        "description": "Google Trends interest over time and related queries with trend direction, momentum, volatility, source proof, API output, scheduling, monitoring, and integrations.",
        "version": "0.1",
        "x-build-id": "uXWDhtPmrjCMUO4WF"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/skootle~google-trends-intelligence/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-skootle-google-trends-intelligence",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/skootle~google-trends-intelligence/runs": {
            "post": {
                "operationId": "runs-sync-skootle-google-trends-intelligence",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/skootle~google-trends-intelligence/run-sync": {
            "post": {
                "operationId": "run-sync-skootle-google-trends-intelligence",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "keywords"
                ],
                "properties": {
                    "keywords": {
                        "title": "Keywords",
                        "minItems": 1,
                        "maxItems": 5,
                        "type": "array",
                        "description": "One to five search terms. Multiple terms are normalized against the same comparison scale.",
                        "items": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 100
                        },
                        "default": [
                            "artificial intelligence",
                            "machine learning"
                        ]
                    },
                    "geo": {
                        "title": "Geography",
                        "maxLength": 10,
                        "type": "string",
                        "description": "Google Trends geo code, such as US, GB, US-CA, or an empty string for worldwide.",
                        "default": "US"
                    },
                    "timeframe": {
                        "title": "Timeframe",
                        "enum": [
                            "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"
                        ],
                        "type": "string",
                        "description": "Google Trends time window.",
                        "default": "today 3-m"
                    },
                    "category": {
                        "title": "Category ID",
                        "minimum": 0,
                        "maximum": 1500,
                        "type": "integer",
                        "description": "Google Trends category ID. Use 0 for all categories.",
                        "default": 0
                    },
                    "property": {
                        "title": "Search property",
                        "enum": [
                            "",
                            "images",
                            "news",
                            "froogle",
                            "youtube"
                        ],
                        "type": "string",
                        "description": "Search surface. An empty value means web search.",
                        "default": ""
                    },
                    "maxRelatedQueries": {
                        "title": "Maximum related queries",
                        "minimum": 0,
                        "maximum": 50,
                        "type": "integer",
                        "description": "Maximum rows from each of the top and rising lists, per keyword.",
                        "default": 25
                    },
                    "includeInterestOverTime": {
                        "title": "Include interest over time",
                        "type": "boolean",
                        "description": "Return normalized timeline points and compute time-series metrics.",
                        "default": true
                    },
                    "includeRelatedQueries": {
                        "title": "Include related queries",
                        "type": "boolean",
                        "description": "Return top and rising related searches for each keyword.",
                        "default": true
                    },
                    "language": {
                        "title": "Language",
                        "minLength": 2,
                        "maxLength": 10,
                        "type": "string",
                        "description": "Google Trends interface locale.",
                        "default": "en-US"
                    },
                    "proxyCountryCode": {
                        "title": "Proxy country",
                        "minLength": 2,
                        "maxLength": 2,
                        "type": "string",
                        "description": "Two-letter country code for the residential proxy exit.",
                        "default": "US"
                    },
                    "maxRetriesPerRequest": {
                        "title": "Maximum attempts per request",
                        "minimum": 1,
                        "maximum": 8,
                        "type": "integer",
                        "description": "Bounded retry count for transient and blocked responses.",
                        "default": 4
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
