# Google Trends Scraper — Fast HTTP, Failed Queries Free (`joshuayan/google-trends-scraper`) Actor

Scrape Google Trends data over pure HTTP — no browser. Interest over time, interest by region, related queries and related topics for any keyword, location and time range. Compare up to 5 terms, batch keywords from a Google Sheet, export flat CSV-ready rows. Failed and partial queries are free.

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

## Pricing

from $0.25 / 1,000 complete google trends queries

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

## Google Trends Scraper — Fast HTTP, Failed Queries Free

Scrape Google Trends data over pure HTTP — no browser. Interest over time, interest by region, related queries and related topics for any keyword, location and time range. Compare up to 5 terms, batch keywords from a Google Sheet, export flat CSV-ready rows. Failed and partial queries are free.

**Pay only for complete successful queries. Partial and failed queries have no Actor event fee; Apify platform usage is charged separately, including unsuccessful attempts.**

### Google Trends Scraper at a glance

This is an independent Google Trends scraper by **joshua yan** on Apify. Its stable Actor identifier is `joshuayan/google-trends-scraper`. It collects Google Trends web data over HTTP and can be run from Apify Console or the Apify API.

| Question | Answer |
| --- | --- |
| What data does it return? | Interest over time, geographic interest, related queries, and related topics when Google supplies them. |
| What inputs does it accept? | Keyword lists, Google Trends explore URLs, or the first column of a public Google Sheet. |
| Can it compare keywords? | Yes, up to five search terms in one comparison query. |
| What can I export? | JSON query results or flat data rows suitable for CSV and spreadsheets. |
| What do the numbers mean? | Relative Google Trends interest, not absolute monthly search volume. |
| What is charged? | One event per complete successful query, including complete no-data responses. Partial and failed queries have no event fee; platform usage is separate. See [pricing](#pricing). |
| What is the compatibility status? | Full output compatibility with the official Apify Actor remains unverified. Validate downstream integrations before migrating production workloads. |

[Try the Actor](https://console.apify.com/actors/tLrOgyBvk3LoaM7LR) · [Input fields](https://apify.com/joshuayan/google-trends-scraper/input-schema) · [API examples](https://apify.com/joshuayan/google-trends-scraper/api/python) · [Report an issue](https://apify.com/joshuayan/google-trends-scraper/issues/open)

### SEO, content research, and GEO planning use cases

- **SEO keyword research:** compare relative interest in candidate search terms, inspect seasonality, and review top or rising related queries. Keep geography and date ranges consistent when comparing results.
- **Content planning:** use related queries and topics to build a research shortlist, then check the underlying results before choosing articles, FAQs, or campaign topics.
- **Regional market research:** compare geographic interest to help prioritize markets. Geographic coverage and resolution depend on the data Google returns.
- **GEO topic research:** use search-interest signals as one input to generative engine optimization content planning. This Actor does not query AI answer engines or measure brand mentions, citations, share of voice, or AI search rankings.
- **Automated research workflows:** call the Actor through the Apify API and pass JSON or flat rows to analysis tools. Review partial-result indicators and the run summary before using the output downstream.

### What this Google Trends Scraper does

Collect interest over time, geographical interest, related queries, and related topics. Use keyword lists, Google Trends URLs, or the first column of a public Google Sheet. Compare up to five terms in one query. Export either one compatible item per query or flat rows for CSV and spreadsheets.

Results are relative Google Trends interest values, not absolute search volumes. Missing optional sections produce a partial result. A query whose curve cannot be retrieved is recorded in `FAILED_QUERIES` without a result item.

### HTTP collection and measured performance

This Actor uses HTTP requests and a reusable session pool, so it does not launch a browser for each query. It also exposes missing sections and supports flat output. Partial results are delivered without an event fee. Exact compatibility across all modes still requires the complete official baseline before production migration.

On October 22, 2026, private run `duxbgu9yKcWSx0P1x` completed 100 distinct queries: 34 worldwide keywords, 33 US URLs, and 33 three-term comparisons. All 100 were complete, with no partial or failed queries in this sample. At 256 MB using US residential exits, P95 was **6.588 seconds including output, state persistence, and charging**; fetching alone was 5.439 seconds. The run made 569 HTTP requests. This is one observed sample, not an availability guarantee.

The official ten-query comparison delivered only one result, so it cannot establish a valid cost or reliability comparison.

### Migrating

The input field names, titles, and selectable values were taken from the official Actor's public build `0.0.141`. Keyword lists and Google Trends URLs can be reused. The browser options `skipDebugScreen`, `maxConcurrency`, `maxRequestRetries`, and `pageLoadTimeoutSecs` are accepted and ignored. Query concurrency, retry policy, and timeout are managed by this Actor.

A live US weather URL has passed a same-query official output shape comparison. Comparison-mode related results currently use a `term` field, which still needs an official comparison baseline: repeated official attempts returned no usable comparison output. Verify downstream consumers before production migration. `missingSections` is an additional field; `relatedTopics_rising` also exists in the observed official output.

### Input

Provide at least one source: `searchTerms`, `startUrls`, or `spreadsheetId`.

| Field | Meaning |
| --- | --- |
| `searchTerms` | Array of keyword strings. Surrounding whitespace and empty entries are removed when queries are expanded. |
| `isMultiple` | Default `false`. When `true`, comma-separated terms in each string form one comparison query, with at most five nonempty terms. |
| `startUrls` | Google Trends explore URLs as `{ "url": "..." }` objects. URLs use their own filters. Missing date, geo, and category default to the past 12 months, worldwide, and all categories; they do not inherit keyword filters. |
| `spreadsheetId` | ID of a public Google Sheet. Reads the first column, skips the header, and ignores blank cells. |
| `timeRange` | Official presets: empty/default (past 12 months), `now 1-H`, `now 4-H`, `now 1-d`, `now 7-d`, `today 1-m`, `today 3-m`, `today 5-y`, or `all`. |
| `customTimeRange` | `YYYY-MM-DD YYYY-MM-DD`, with real dates and start no later than end. Overrides the preset for keyword and Sheet queries. |
| `geo` | Data geography; empty means worldwide. A URL may specify a country or subregion. |
| `category` | Category selected in the input form, sent as a string such as `"5"` (Computers & Electronics). Empty means all categories. A Trends URL can specify other category IDs using its `cat` parameter. |
| `outputMode` | `compatible` (default) or `flat`. |
| `maxItems` | Maximum number of queries that produce output. `0` means unlimited. In flat mode this limits queries, not rows. |
| `viewedFrom` | Country code for the Apify residential proxy IP; case-insensitive, defaults to US. Independent of data geography. |

Single keyword:

```json
{ "searchTerms": ["webscraping"], "maxItems": 10 }
```

Comparison:

```json
{
  "searchTerms": ["chatgpt,gemini,claude"],
  "isMultiple": true,
  "timeRange": "today 3-m",
  "outputMode": "flat"
}
```

From a URL:

```json
{
  "startUrls": [
    { "url": "https://trends.google.com/trends/explore?q=coffee&geo=US&date=today%203-m" }
  ]
}
```

### Output

#### Compatible

One item per complete or partial query. The following illustrates the structure and is not a measured live result:

```json
{
  "inputUrlOrTerm": "webscraping",
  "searchTerm": "webscraping",
  "interestOverTime_timelineData": [],
  "interestOverTime_averages": [],
  "interestBy": [],
  "interestBySubregion": [],
  "interestByCity": [],
  "interestByMetro": [],
  "relatedQueries_top": [],
  "relatedQueries_rising": [],
  "relatedTopics_top": [],
  "relatedTopics_rising": [],
  "missingSections": []
}
```

All four geography arrays are present. COUNTRY uses `interestBy`; REGION populates `interestBySubregion` and an additional CITY request populates `interestByCity`. CITY uses `interestByCity`; DMA uses `interestByMetro`, verified with a California URL. Unused arrays are empty.

Timeline points include `time`, `formattedTime`, `formattedAxisTime`, `value`, `hasData`, and `formattedValue`, preserving optional `isPartial` when Google returns it. Related entries contain a query or topic, values, and `term` for the source comparison term. `missingSections` contains missing optional section names: `region`, `relatedQueries`, or `relatedTopics`. The curve is required for output.

#### Flat

One row per data point, with 27 columns (`schemaVersion: 2`). The original 11 columns remain first in the flat dataset view and CSV export: `queryId`, `searchTerm`, `section`, `listType`, `time`, `geoCode`, `geoName`, `label`, `value`, `formattedValue`, and `isBreakout`.

The following 16 columns are appended:

| Columns | Meaning |
|---|---|
| `sourceTerm` | Source keyword for the row; `null` when attribution is unavailable. |
| `topicId`, `topicType` | Google topic identity and type; `null` for non-topic rows or unavailable data. |
| `geoResolution` | `COUNTRY`, `REGION`, `CITY`, or `DMA` for region rows; otherwise `null`. |
| `hasData`, `isPartial` | Upstream data availability and timeline bucket quality. `isPartial` describes a time bucket, not the whole query. |
| `schemaVersion`, `source` | `2` and `google-trends-web`. |
| `inputUrlOrTerm`, `geo`, `timeRange`, `category`, `property` | Effective query identity and filters. |
| `retrievedAt` | UTC retrieval timestamp, persisted with the rows and preserved on recovery. |
| `queryStatus`, `missingSections` | Existing query processing status and detected missing sections. These do not guarantee that Google supplies nonempty related topics. |

`section` is `timeline`, `region`, `relatedQuery`, or `relatedTopic`; `listType` is `top`, `rising`, or `null`. The default `compatible` output and per-query event fee are unchanged. More columns increase export size and can add storage and transfer usage; flat rows do not incur an extra Actor event fee. Select the **Flat rows** dataset view, or use the dataset API with `format=csv&view=flat`, to export the ordered columns.

Example timeline row:

```json
{
  "queryId": "q0",
  "searchTerm": "weather",
  "schemaVersion": 2,
  "source": "google-trends-web",
  "inputUrlOrTerm": "weather",
  "geo": "US",
  "timeRange": "2026-07-01 2026-09-30",
  "category": 0,
  "property": "",
  "retrievedAt": "2026-10-23T08:49:18.930Z",
  "queryStatus": "succeeded",
  "missingSections": [],
  "section": "timeline",
  "listType": null,
  "time": "1782864000",
  "geoCode": null,
  "geoName": null,
  "label": "weather",
  "value": 71,
  "formattedValue": "71",
  "isBreakout": false,
  "sourceTerm": "weather",
  "topicId": null,
  "topicType": null,
  "geoResolution": null,
  "hasData": true,
  "isPartial": false
}
```

#### Run summary and recovery

The `OUTPUT` key contains `totalQueries`, `succeeded`, `partial`, `failed`, `charged`, and `stoppedBy` (`null`, `maxItems`, `chargeLimit`, or `chargeUncertain`). `FAILED_QUERIES` contains `queryId`, `input`, `reason`, and `attempts`.

Each query's prepared result and dataset position are persisted before output. Resuming the same run checks the existing rows and appends only a missing suffix, including interrupted flat batches. Already fetched results are reused. Keep the same query inputs and output mode; changed inputs are rejected. Do not append to the run's dataset from another writer while it is running or being resumed. Conflicting rows stop recovery instead of being overwritten.

A per-query charging ledger records the intent and receipt. A completed receipt is reused on resume; a delivered query whose charge was not attempted can finish charging without another fetch. If a charge response is lost, the Actor checks the run's charged-event counter. When confirmation remains ambiguous, it stops with `stoppedBy: "chargeUncertain"`, retains `pendingCharge` in `RUN_STATE`, and does not send another charge. Resume the same run to retry confirmation. A stop before sending the charge can also be ambiguous; automated recovery cannot guarantee a charge in that case. `CHARGE_<queryId>` records the reconciliation state. Legacy runs without these journals retain their earlier recovery limits.

### Pricing

**Base price: $2 per 1,000 complete queries ($0.002 per query), plus Apify platform usage costs.** There is no separate Actor start fee or flat-row event fee.

#### Introductory plan discounts

Introductory discounts took effect on October 23, 2026. We plan to review these rates after the initial 60-day period. Check the [live Pricing tab](https://apify.com/joshuayan/google-trends-scraper/pricing) for the price that applies to your plan before starting a run.

| Discount tier | Discount from base price | Per complete query | Per 1,000 complete queries |
| --- | ---: | ---: | ---: |
| FREE | 0% | $0.002 | $2.00 |
| BRONZE | 60% | $0.0008 | $0.80 |
| SILVER | 80% | $0.0004 | $0.40 |
| GOLD | 87.5% | $0.00025 | $0.25 |
| PLATINUM (enterprise) | 87.5% | $0.00025 | $0.25 |
| DIAMOND (enterprise) | 87.5% | $0.00025 | $0.25 |

Enterprise tiers currently use the same event price as GOLD. These are event fees only. Any later price increase will follow Apify's notification period; the offer does not automatically revert on day 60.

The 100-query benchmark consumed $0.05708758 of platform usage. At the undiscounted base event rate, the same usage would total approximately $0.25709 for 100 complete queries, or $2.571 per thousand. This is a projection: actual proxy, storage, compute, and retry costs vary by workload and Apify plan. The earlier benchmark used a lower private test event price; it is not the public event price. The incomplete official run is excluded from valid unit-cost comparison.

One `trends-query` event is charged for each complete successful query, including a complete response with no data. Partial results, failed queries, and input validation failures do not charge this event. `charged` in the summary counts queries with an accepted event charge.

Apify platform costs, including residential proxy traffic and compute, are separate from the event fee and may be incurred by unsuccessful attempts. Once the event budget is reached, no new queries are claimed; queries already running can finish and produce uncharged output. Local SDK test pricing is a simulation, not the production price.

### FAQ

**Is this the official Google Trends API?** No. This is an independent community Actor that collects data from Google Trends web endpoints. Apify provides the API used to run the Actor. It is not an official Google product or a complete drop-in replacement for the official Apify Google Trends Actor.

**Can I get Google Trends data as CSV?** Yes. Choose `outputMode: "flat"` to produce one row per data point, then export the dataset as CSV. A query can produce many rows; the event fee is per complete query, not per exported row.

**Does this measure monthly keyword search volume?** No. Results are relative Google Trends interest values. They should not be presented as exact search counts, forecasts, or advertising keyword volumes.

**Can this track visibility in ChatGPT, Gemini, or other AI answers?** No. It can supply topic-demand research for a GEO workflow, but it does not retrieve AI answers or measure their mentions and citations. Its `geo` input means data geography, not generative engine optimization.

**Are failed Google Trends queries completely free?** They have no Actor event fee, and partial results also have no event fee. Apify compute, proxy, storage, and other platform usage can still be charged for these attempts.

**How are 429 responses handled?** The session is retired and requests across the session pool share a cooldown. Cooldown increases from 5 to at most 30 seconds, also considering Google's `Retry-After` header within that bound. HTML block pages and Google consent/sorry redirects receive the same treatment. Requests allow up to three retries; JSON parse failures allow one retry. An optional widget that still fails with a transient or rate-limit error gets one final recovery attempt, without fetching the curve or successful widgets again. Persistent failures produce a failed or partial query. Waiting and retries can increase latency; this does not guarantee freedom from rate limits.

**Can I compare more than five terms?** No. Split them into groups of at most five. Each group counts as one query and its timeline contains one value per term.

**Why are geography arrays empty?** All four fields are retained for compatibility; only the geographic resolution Google supplies is populated. This Actor requests CITY as well when Google supplies a REGION widget. A US-CA URL may return DMA/metro data.

**Why is my Sheet rejected?** It must be publicly readable without signing in and contain a header plus at least one nonblank value in the first column. Quoted CSV cells, commas, and embedded newlines are supported.

**Can I use custom proxies?** By default, the Actor uses Apify residential proxies. `TRENDS_PROXY=none` enables direct local debugging. A custom HTTP(S) proxy URL is supported through `TRENDS_PROXY`; `viewedFrom` only controls Apify residential proxies.

**Is this a drop-in replacement for the official Apify Actor?** No. Private performance, real response parsing, and one US same-query official shape comparison are verified. Complete official-output compatibility and a valid ten-query cost comparison remain pending. In particular, comparison-mode related-item formatting still needs an official baseline.

# Actor input Schema

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

Keyword list. Provide searchTerms, Google Trends startUrls, or a public spreadsheetId.

## `isMultiple` (type: `boolean`):

If checked, a comma is treated as a separator for multiple terms

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

Choose a predefined search's time range (defaults to 'Past 12 months')

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

Get results from a specific geo area (defaults to 'Worldwide'). This maps to different fields in the dataset results: smaller countries like Greece return just 'interestByCity' data, while larger ones like the United States return 'interestBySubregion' data as well. When omitted (Worldwide), 'interestBy' is returned instead, which corresponds to the 'Interest by area' section on Google Trends. Note: When using Google Trends URLs from the '🔗 Use a Google Trends URL instead of search terms' section, the geo parameter from the URL's query string takes precedence over this setting. You can also use start URLs to retrieve 'interestByMetro' data for metropolitan areas by providing URLs with more specific geo regions like California (US) directly copied from Google Trends.

## `viewedFrom` (type: `string`):

Country for residential proxy exits, independent of data geography. Defaults to US; country codes are case-insensitive.

## `skipDebugScreen` (type: `boolean`):

Legacy browser option. Accepted for compatibility and ignored by this HTTP Actor.

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

Copy a Google Trends URL and paste it here.

## `spreadsheetId` (type: `string`):

ID of a publicly readable Google Sheet. Reads the first column, skips its header and blank cells, and ignores other columns.

## `category` (type: `string`):

Choose a category to filter the search for (defaults to 'All categories')

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

Maximum number of queries that produce output. Zero means no limit. In flat mode this limits queries, not rows.

## `customTimeRange` (type: `string`):

Provide custom time range. If present, it takes precedence over regular timeRange. Format is: `YYYY-MM-DD YYYY-MM-DD`. See examples in the documentation.

## `maxConcurrency` (type: `integer`):

Legacy browser option. Accepted for compatibility and ignored by this HTTP Actor.

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

Legacy browser option. Accepted for compatibility and ignored by this HTTP Actor.

## `pageLoadTimeoutSecs` (type: `integer`):

Legacy browser option. Accepted for compatibility and ignored by this HTTP Actor.

## `outputMode` (type: `string`):

Compatible returns one item per query; flat returns one row per data point.

## Actor input object example

```json
{
  "searchTerms": [
    "webscraping"
  ],
  "isMultiple": false,
  "timeRange": "today 3-m",
  "geo": "",
  "skipDebugScreen": false,
  "startUrls": [
    {
      "url": "https://trends.google.com/trends/explore?q=web+scraping&geo=US"
    }
  ],
  "spreadsheetId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
  "category": "",
  "maxItems": 0,
  "customTimeRange": "2024-01-01 2024-12-31",
  "maxConcurrency": 10,
  "maxRequestRetries": 7,
  "pageLoadTimeoutSecs": 180,
  "outputMode": "compatible"
}
```

# Actor output Schema

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

Complete and partial query results; compatible objects or flat rows according to outputMode.

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

Counts of complete, partial, failed and charged queries, plus the stop reason.

# 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": [
        "webscraping"
    ],
    "timeRange": "",
    "viewedFrom": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("joshuayan/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 = {
    "searchTerms": ["webscraping"],
    "timeRange": "",
    "viewedFrom": "",
}

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

```

## MCP server setup

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