# Google Trends Scraper - Reliable Interest, Regions & Trending (`lanternlab318/google-trends`) Actor

Google Trends without login: interest over time, interest by region, top and rising related queries, and the real-time Trending now list with search volume and news URLs. Compare up to 5 terms. Refused requests are retried and reported. Empty results, partial rows and duplicates are free.

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

## Pricing

$1.49 / 1,000 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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Trends Scraper

Search interest from Google Trends, as data: the **interest-over-time** series, the **breakdown by region**, the **top and rising related queries**, and the real-time **"Trending now"** list with each trend's search volume, start time and news article URLs. Up to five terms compared in one chart. No Google login, no cookies of yours, no account. **$1.49 per 1,000 rows, no start fee.** See [Pricing](#pricing).

Input:

```json
{
  "searchTerms": ["bitcoin", "ethereum"],
  "geo": "US",
  "timeRange": "today 12-m",
  "includeRegions": true,
  "includeRelated": true,
  "trendingNowGeo": "US"
}
```

Output — one row per data point. Status rows and the run summary are free. Samples from a real run:

```json
{
  "recordType": "interest_over_time",
  "keyword": "bitcoin",
  "geo": "US",
  "timeRange": "today 12-m",
  "category": 0,
  "date": "2026-10-11T00:00:00.000Z",
  "formattedTime": "Oct 11 – 17, 2026",
  "value": 15,
  "hasData": true,
  "isPartial": false,
  "comparisonIndex": 0,
  "comparisonTerms": ["bitcoin", "ethereum"],
  "sourceUrl": "https://trends.google.com/trends/explore?q=bitcoin&date=today+12-m&geo=US",
  "partial": false,
  "partialReason": null,
  "collectedAt": "2026-10-24T18:05:11.204Z"
}
```

```json
{
  "recordType": "trending_now",
  "title": "knicks vs celtics",
  "geo": "US",
  "searchVolume": 500000,
  "growthPercent": 1000,
  "startedAt": "2026-10-24T09:30:00.000Z",
  "rank": 1,
  "relatedQueries": ["knicks vs celtics", "celtics vs knicks", "knicks game"],
  "newsUrls": ["https://www.example-sports-site.com/knicks-celtics-recap/"],
  "newsSource": "daily_trends_rss",
  "listSource": "trending_now_list",
  "trendingWindowHours": 24,
  "sourceId": "US:knicks vs celtics",
  "partial": false,
  "partialReason": null,
  "collectedAt": "2026-10-24T18:05:13.880Z"
}
```

Limits:

- **Public data only.** What the public Google Trends pages show: no login, no cookies you supply, no account, no personal data. `value` is Google's relative interest, 0–100 — Google Trends publishes no absolute search counts (the trending list is the one exception, where Google itself gives a rounded volume).
- **Every section gets a status row saying what happened.** A keyword Google has no data for is an empty result; a section cut short by a limit, a refusal or an error is `partial` with the reason — the two are never confused.
- **Your limits stop collection and appear in the rows**: `maxTrendingNow`, the `maxUpstreamRequests` request ceiling and the maximum charge per run (see [Pricing](#pricing)).

### What it does

- **Interest over time** — one row per time bucket per keyword, with Google's own bucket label and `isPartial` on the bucket that is still running, so a half-finished week is never read as a drop.
- **Interest by region** — one row per region, with Google's region code (`US-NY`, `RU-MOW`), its name in your language and its value, 0–100 against that keyword's own strongest region. Ask for a country and you get its states or oblasts; ask for worldwide and you get countries.
- **Related queries** — the top list for the period and the rising list, where `value` is the percentage increase and `formattedValue` can read `Breakout`.
- **Trending now** — the real-time list for one country: Google's rounded search volume, when the trend started rising, the queries grouped under it, and the URLs of the news articles Google shows with it.
- **Comparison groups** — up to five terms in one chart, with each term's own series and regional breakdown, each row labelled with its keyword, its position in the group (`comparisonIndex`) and the group it was normalised against (`comparisonTerms`), because values from different groups are not comparable.
- **Stable by construction** — a refused request is retried from a different exit IP, and the run summary reports exactly how often that happened (`blockedHits`, `retries`, `sessionRotations`), so stability is measured, not claimed.
- **Honest about gaps** — deduplicated within the run, so a point is written and charged once; a section Google answers with nothing says so; anything incomplete is `partial` with a reason. Partial rows are free, and so is a bucket or region Google reports no data for (`hasData: false`), which is what a term with no search volume inside a comparison group gets.

### What it does not do

- No absolute search volumes for keywords: Google Trends does not publish them. The relative 0–100 series is what the platform offers.
- No Google Ads keyword-planner data, no competitor's own metric, no forecasting.
- No Google Trends subpages beyond the sections above (no YouTube/Shopping/Image property filters, no year-in-search pages).
- No scheduling or monitoring built in; use Apify schedules.

### Input

| Field | Default | Meaning |
| --- | --- | --- |
| `searchTerms` | — | One to five terms, compared in one chart. Leave empty and set `trendingNowGeo` to collect only the trending list. |
| `geo` | `""` (worldwide) | Google Trends region: `""`, `US`, `RU`, `DE`, or a subregion such as `US-NY`, `RU-MOW`. |
| `timeRange` | `today 12-m` | `now 1-H`, `now 4-H`, `now 1-d`, `now 7-d`, `today 1-m`, `today 3-m`, `today 12-m`, `today 5-y`, `all`. |
| `category` | `0` | Google Trends category number, `0` for all categories. |
| `includeRegions` | `true` | Collect the breakdown by region. |
| `includeRelated` | `true` | Collect the related queries (and related topics, which Google currently returns empty — see [Limits](#limits-and-boundaries)). |
| `language` | `en-US` | Response language, BCP-47. Decides the language of region names and trend titles. |
| `trendingNowGeo` | `""` | Two-letter country code for the real-time trending list, for example `US`, `RU`. Empty skips it. |
| `trendingNowHours` | `24` | Trending window: `4`, `24`, `48` or `168` hours. |
| `maxTrendingNow` | `100` | Upper bound on trending rows. |
| `maxUpstreamRequests` | derived | Hard ceiling on requests to Google Trends for the whole run, retries included. Leave empty and the Actor works one out from your input. |
| `proxyConfiguration` | Apify Proxy | The proxy the requests go through. The datacenter default is what this Actor is measured on. |

### Output

`recordType` tells the rows apart. Every data row carries `keyword`, `geo`, `timeRange`, `category`, `sourceUrl`, `partial`, `partialReason` and `collectedAt`.

| `recordType` | Key fields |
| --- | --- |
| `interest_over_time` | `date`, `timestamp`, `formattedTime`, `value` (0–100), `hasData`, `isPartial`, `comparisonIndex`, `comparisonTerms` |
| `interest_by_region` | `geoCode`, `geoName`, `value`, `hasData`, `resolution` (`COUNTRY`, `REGION`, `DMA`, `CITY`), `comparisonIndex` |
| `related_query` | `query`, `type` (`top` / `rising`), `value`, `formattedValue`, `rank` |
| `related_topic` | `topic`, `topicMid`, `topicType`, `type`, `value`, `formattedValue`, `rank` |
| `trending_now` | `title`, `searchVolume`, `growthPercent`, `startedAt`, `relatedQueries`, `newsUrls`, `news`, `newsSource`, `listSource`, `rank`, `sourceId` |

One row per section read, `recordType: "section_status"`, never charged:

| Field | Meaning |
| --- | --- |
| `section` | `interest_over_time`, `interest_by_region`, `related_queries`, `related_topics` or `trending_now`. |
| `rowsReturned` / `rowsCharged` | Rows this section wrote and charged. |
| `duplicatesSkipped` | Rows the run had already collected. |
| `partial` / `partialReason` | `true` with `upstream_request_cap`, `charge_limit_reached`, `blocked`, `upstream_error`, `section_unavailable` or `not_collected` when the section is incomplete. |
| `statusText` | The same thing in a sentence, so the dataset explains itself. |

The last row is `recordType: "RUN_SUMMARY"` — rows collected and charged per record type, empty sections, partial sections by reason, requests sent per endpoint, refusals and retries, bytes moved, and why collection stopped. The same object is in the run's key-value store under `RUN_SUMMARY`.

#### Partial is not empty

`rowsReturned: 0` with `partial: false` means Google Trends has no data for that keyword — a term with little search volume legitimately has none. `partial: true` means the section is incomplete and there may well be data. A run that collected nothing *because* a request was refused, failed or ran out of the run's own budget fails rather than reporting an empty success; a run whose keywords genuinely have no data succeeds with zero rows.

### Limits and boundaries

- Only Google Trends' public data. No login, no cookies you supply, no account, no personal fields.
- `value` is relative interest within one chart, 0–100. Two runs with different terms, regions or windows are normalised differently, so their values are not comparable; `comparisonTerms` records the group each row belongs to.
- **Related topics**: Google's public related-topics endpoint has been answering with an empty list for every term measured (2026-10-24), including entity ids. The section is collected and reported, and its status row says `rowsReturned: 0` as an empty result rather than inventing rows. Related *queries* are returned normally.
- The last bucket of a series is live: `isPartial: true` means its value will still change.
- The trending list's `searchVolume` is Google's own rounded figure. **`newsUrls` come from the public daily-trends feed**, which carries the strongest ten to twenty trends of a country, while the real-time list carries hundreds: a trend the feed does not carry has `newsSource: "not_in_daily_trends_feed"` and an empty `newsUrls`, never invented links. `listSource` says which of the two public lists a row came from; a feed-only row has the articles but no exact `searchVolume` or query breakdown, and those fields are `null` rather than guessed.
- A country Google keeps no trending list or daily-trends feed for (it answers HTTP 400) is reported as an empty result rather than as a failure, and is not retried.
- `startedAt` is Google's own time for a trend; `collectedAt` is ours. They are separate fields and neither is derived from the other.

### Pricing

Pay per event, no start fee:

| Event | Price | Charged for |
| --- | --- | --- |
| `result` | $0.00149 ($1.49 per 1,000) | Each data row that carries data. Section status rows, empty results, partial rows, rows Google reported `hasData: false` for and the run summary are free. |

Set a maximum charge for the run and the Actor stops before going over it.

# Actor input Schema

## `searchTerms` (type: `array`):

One to five search terms, compared in one chart the way Google Trends compares them. With more than one term the values of the series and of the regional breakdown are normalised across the group, so they can be read against each other. Leave this empty and set "Trending now country" below to collect only the real-time trending list.

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

Google Trends region code: empty for worldwide, "US", "RU", "DE" for a country, or a subregion such as "US-NY" or "RU-MOW". The regional breakdown is then one level below what you ask for: countries for worldwide, states or oblasts for a country.

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

The window the series and the related lists cover. Google's own resolution follows the window: minutes for the hourly windows, days for a few months, weeks for a year, months for five years.

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

Google Trends category number to restrict the search to, 0 for all categories. The numbers are Google's own (for example 7 Finance, 71 Food & Drink, 174 Beauty & Fitness).

## `includeRegions` (type: `boolean`):

Collect the regional breakdown as well: one row per region Google reports, with its code, its name and its value.

## `includeRelated` (type: `boolean`):

Collect the related lists as well: the top queries for the period and the fastest-rising ones, plus the related topics when Google offers them for the term.

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

Interface language of the response, as a BCP-47 tag: "en-US", "ru-RU", "de-DE". It decides the language of region names and of the trending titles, not which data Google has.

## `trendingNowGeo` (type: `string`):

Two-letter country code to collect the real-time "Trending now" list for, for example "US" or "RU". Each trend comes with Google's rounded search volume, when it started rising, the queries grouped under it and the URLs of the news articles Google shows with it. Leave empty to skip this list.

## `trendingNowHours` (type: `string`):

How far back the trending list reaches.

## `maxTrendingNow` (type: `integer`):

Upper bound on the rows the trending list may produce. Google returns a few hundred trends for a large country.

## `maxUpstreamRequests` (type: `integer`):

Hard ceiling on the HTTP requests this run sends to Google Trends: the cookie handshake, the explore call, every section and every retry together. At the ceiling collection stops - the sections not reached are written with partialReason "upstream_request_cap" and the run then ends normally. Leave empty and the Actor works a ceiling out from your input, so a run is never unbounded.

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

The proxy the requests to Google Trends go through. The default (Apify Proxy, datacenter) is the cheapest and is what this Actor is measured on. A refused request is retried from a different exit IP automatically; pick residential groups here only if you see refusals on your own account.

## Actor input object example

```json
{
  "searchTerms": [
    "bitcoin"
  ],
  "geo": "",
  "timeRange": "today 12-m",
  "category": 0,
  "includeRegions": true,
  "includeRelated": true,
  "language": "en-US",
  "trendingNowGeo": "",
  "trendingNowHours": "24",
  "maxTrendingNow": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `runSummary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchTerms": [
        "bitcoin"
    ]
};

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

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

```

## MCP server setup

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

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/zsrsdAeUMl4dthM1V/builds/PkUDfstKQgNMmYbyQ/openapi.json
