# Google Keyword Search Volume & Seasonality (`automation_craft/google-keyword-search-volume`) Actor

Get Google keyword search volume by month: 48 months of Google Ads data per keyword with CPC, competition, bids, seasonal peaks and a rank for the months you plan for. Keyword ideas, website keywords, any country or city. Keywords without data are free. JSON, CSV or API.

- **URL**: https://apify.com/automation_craft/google-keyword-search-volume.md
- **Developed by:** [Automation Craft](https://apify.com/automation_craft) (community)
- **Categories:** SEO tools, Marketing, E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.23 / 1,000 keywords

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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 Keyword Search Volume & Seasonality

**Google keyword search volume** for your keyword lists, keyword ideas and websites: up to **48 months of monthly searches** per keyword from Google Ads (Keyword Planner data), with CPC, competition and top of page bids, plus what the numbers mean for your calendar: the **seasonal peak months**, the trend, and a **rank of your keywords for each of the next 1 to 3 months** you plan for. Any Google Ads location: countries, regions, cantons, states and cities. No Google Ads account and no API key needed.

Built for online sellers, Shopify and marketplace merchants, SEO and PPC teams and automation builders who decide which products, listings and campaigns to prepare before the season starts.

#### What makes it different

- **48 months, labelled.** Every month is written `YYYY-MM` with its year and month number, oldest first, and a month Google did not report is listed in `missingMonths` instead of being left out.
- **Seasonality computed for you.** A seasonal index per calendar month, the peak and low months, how strong the season is, and in how many years the peak month really was the top month.
- **A plan for the months ahead.** For each planned month every keyword row carries the historical average for that calendar month, last year's figure and the change against the latest month, and one plan row per month ranks all keywords of the run. The historical evidence and the planning assumption are two separate texts. Past search demand is history, not a forecast.
- **Fair billing.** A keyword without Google data is a free row, and so is everything delivered again from your 30 day memory. A lookup that finds no keyword with data is free.
- **CSV for sheets.** `MONTHLY_SEARCHES.csv` has one line per keyword per month (long format) and `PLANNER.csv` the ranks per planned month, ready for Google Sheets or Excel.

### Quick start

1. Paste your keywords (one per line), or seed keywords with **What to look up** set to keyword ideas.
2. Pick one or more **Locations**: a country name, a two letter code (CH), Worldwide, or a region or city the way Google Ads writes it (`Zurich,Switzerland`).
3. Click **Start**. A queued lookup is usually ready in 10 to 60 seconds. Export the dataset as JSON, CSV or Excel, or open the two CSV files in the run's key-value store.

**Search volume for a keyword list in Switzerland:**

```json
{
    "keywords": ["winterjacke", "regenjacke", "badehose", "veste d'hiver", "maillot de bain"],
    "locations": ["Switzerland"]
}
```

**Keyword ideas for a seed, German, at most 200 ideas:**

```json
{
    "mode": "ideas",
    "keywords": ["winterjacke"],
    "locations": ["Switzerland"],
    "language": "de",
    "maxKeywords": 200
}
```

**The keywords for a website, ranked by search volume:**

```json
{
    "mode": "website",
    "websites": ["example.com"],
    "locations": ["United States"],
    "sortBy": "search_volume",
    "maxKeywords": 500
}
```

### What you get

One **keyword row** per keyword and location, one **plan row** per planned month, free **status rows** for anything that was refused or skipped, and a **summary row** at the end.

| Field | What it holds |
|---|---|
| `keyword` | The keyword as Google Ads keys it (lower case) |
| `dataStatus` | `ok` (Google has data, the row is charged unless it came from the memory) or `no_data` (free) |
| `searchVolume` | Google Ads average monthly searches (last 12 months) |
| `monthlySearches` | Oldest first: `month` (YYYY-MM), `year`, `monthNumber`, `searches` |
| `missingMonths`, `monthsWithData` | Months of the window Google did not report |
| `cpc`, `competition`, `competitionIndex`, `lowTopOfPageBid`, `highTopOfPageBid`, `currency` | Google Ads cost per click, competition and top of page bids (USD) |
| `emptyFields` | The metric fields Google left empty on a row with data |
| `seasonality` | `seasonalIndex` and `monthlyAverages` per calendar month, `peakMonth`, `peakMonths`, `lowMonth`, `lowMonths`, `strength`, `label` (steady, seasonal, strongly seasonal), `peakConsistency`, `cyclesCovered` |
| `trend` | `yoyChangePct`, `qoqChangePct`, `momChangePct`, `last3MonthsVsYearBeforePct`, `last12MonthsTotal`, `previous12MonthsTotal`, `direction` (growing, stable, declining), `latestMonth` |
| `planning` | Per planned month: `historicalAverage`, `seasonalIndex`, `lastYearSearches`, `changeFromLatestPct`, `historicalEvidence`, `planningAssumption` |
| `locationCode`, `locationName`, `locationType`, `countryCode`, `language`, `searchPartners` | The targeting of the row |
| `dateFrom`, `dateTo`, `dataAsOf`, `retrievedAt`, `source`, `sourceEndpoint`, `lookupId` | The month window, the date Google last updated its keyword data, when the row was read and where it came from |
| `closeVariants`, `spellCorrectedTo` | Keywords of the same lookup that Google gave the same figures (close variants), and Google's spelling |
| `discoveredBy`, `seedKeywords`, `isSeed`, `extraWords`, `extraWordsNote`, `conceptGroups`, `discovery` | For ideas and website keywords: the source, the seeds it matches, the words it adds to them, Google's concept groups, and for ranked keywords the ranking position, page, title and estimated traffic |
| `inputPositions` | Where the keyword stood in your list |
| `fromMemory`, `memoryDeliveredOn` | Delivered again from your 30 day memory (free) |
| `insights` (add-on) | `keywordDifficulty` (0 to 100), `searchIntent`, `otherIntents`, `serpFeatures`, `googleResultsCount`, `coreKeyword`, `categories`, `longHistory` (about 8 years of months), `insightsUpdatedAt` |
| `aiSearchVolume`, `aiMonthlySearches` (add-on) | How often the keyword is asked in AI tools, 12 months |
| `bing` (add-on) | Bing `searchVolume`, `cpc`, `competition` (a number from 0 to 1, where Google Ads gives a level and an index), `monthlySearches` (24 months) |

**Words of demand, not product facts.** In ideas, `extraWords` lists what a keyword adds to its seed (damen, wasserdicht, lang). They describe what people search for. Use one in a product title only when the product really has it: this Actor never says what a product is.

#### Measured fill

| Field | Head keywords: 86 rows of the platform matrix (US, CH, CA, GB; 2026-10-18) | Keyword ideas, long tail: 8,180 ideas of 3 Swiss seeds (2026-10-18) |
|---|---|---|
| `searchVolume` and 48 `monthlySearches` | 86 of 86 (100 percent), every month of the window filled | 8,163 of 8,180 (99.8 percent); 17 ideas without figures are left out |
| `cpc` | 86 of 86 (100 percent) | 5,181 of 8,180 (63 percent) |
| `competition`, `competitionIndex` | 86 of 86 (100 percent) | 6,135 of 8,180 (75 percent) |
| `lowTopOfPageBid`, `highTopOfPageBid` | 83 of 86 (96.5 percent) | 3,208 of 8,180 (39 percent) |
| `seasonality` with a seasonal index | 86 of 86 (67 strongly seasonal, 15 seasonal, 4 steady) | computed for every idea with 12 or more months |
| `insights` (add-on) | intent on the rows the Labs database holds and keyword difficulty on most of them (of 50 Swiss test keywords: 38 held, with intent on 38 and difficulty on 35; long tail and nonsense ones left out) | |

Sources: the platform runs of `platform-results/matrix-t1.json` and the feasibility spike (`FEASIBILITY.md`). Long tail keywords carry fewer bids and CPC figures because Google Ads has no auction data for them.

#### Seasonality and planning, and what they are not

The seasonal index of a month is that calendar month's average searches divided by the average month, over the whole 12 month cycles of the window (48 months by default gives 4 cycles). A month at 1.25 or more is a peak month. `peakConsistency` says in how many full calendar years the peak month was really that year's top month. The planned months are, by default, the three months after the month the run starts; `planFrom` and `planningMonths` change them. Every planned month carries two texts: `historicalEvidence` (what the history shows) and `planningAssumption` (that the month will follow that history). The data is past search demand: it is not a forecast of demand or sales.

### How much does it cost to get Google keyword search volume?

You pay per result with Apify's pay per event pricing. Prices in USD:

| Event | When it is charged | FREE | BRONZE | SILVER | GOLD |
|---|---|---|---|---|---|
| Keyword | One keyword row with Google data | 0.00032 | 0.00029 | 0.00027 | 0.00023 |
| Lookup | One Google Ads or Bing request (up to 1,000 keywords, or one set of ideas) that delivered a charged keyword | 0.36 | 0.33 | 0.30 | 0.26 |
| Fast lookup | Fast speed only: one paid request that was sent live | 0.21 | 0.19 | 0.17 | 0.15 |
| Keyword insight | Add-on, one charged row with insights | 0.00084 | 0.00077 | 0.0007 | 0.0006 |
| Discovered keyword | One charged row found through related searches, suggestions, autocomplete or ranked keywords | 0.00084 | 0.00077 | 0.0007 | 0.0006 |
| AI search volume | Add-on, one charged row with an AI search volume | 0.00084 | 0.00077 | 0.0007 | 0.0006 |
| Actor start | Once per run | 0.40 | 0.40 | 0.40 | 0.40 |

Platinum and Diamond plans pay the Gold price. The Store pricing card shows these same prices per 1,000 events: "$0.23 / 1,000" on the Keyword row means one keyword costs $0.00023, and "$260.00 / 1,000" on the Lookup row means one lookup costs 26 cents.

**What a run costs** (Gold plan): 3 keywords cost 0.40 (start) + 0.26 (one lookup) + 3 x 0.00023 = **0.6607**. 1,000 keywords in one lookup cost 0.40 + 0.26 + 0.23 = **0.89**. 5,000 keywords take five lookups: 0.40 + 5 x 0.26 + 1.15 = **2.85**. Keyword ideas: 1,000 ideas from one lookup cost 0.89, like 1,000 keywords. The price does not depend on how many months you ask for.

**Never charged as events** (the start fee is charged at every run, refused ones included): keywords without Google data, rows delivered again from your memory, filtered keywords, refused input, the plan rows, the status rows and the summary, a lookup that finds no keyword with data, a close variant left out by Merge close variants, and an add-on for a keyword the data source does not hold. A run whose lookups keep finding nothing stops after what its start fee can carry, with a free row that says so (`spend_bound`); free rows past what the run's price can carry are not written, and a status row says how many (`free_rows_capped`, `memory_rows_capped`).

**Spending control:** set `spendingLimitUsd`, or the run option Max total charge; the run stops before its charges would pass it; the keywords it did not reach are counted in the summary (`keywordsNotLookedUp`, and `rows.withheldByChargeLimit` for keywords looked up but held back by the limit), and `resumeFromPosition` names the first list position to run again. An add-on is bought only for the keywords whose add-on event still fits the limit, and only while the run's price carries its request; an add-on request it does not carry is skipped with a free row (`addon_not_bought`), and the keyword rows go out without it. With more locations or lookups still ahead in the run, an add-on request that would leave too little room for the next lookup gives way first, so the keyword rows of every location are looked up before add-ons. The free row names which of the two happened and what to do: run that location on its own, or run the keywords again with the add-on in a smaller run.

**Restarts.** When the platform restarts a run (a migration), the run goes on from its ledger without buying or charging anything twice; its summary counts the rows and charged events of the whole run, and its outcome is `partial` with a note, since a lookup or add-on in flight at the restart may be missing. If the restarted run cannot read its dataset back, it stops (`restart_read_failed`) and `rowCountsCover` says its row counts cover that attempt only.

### Input

| Setting | What it does |
|---|---|
| `mode` | `volume` (your keywords), `ideas` (ideas from seeds, seeds included), `website` (keywords for websites) |
| `keywords`, `keywordsText` | Keywords or seeds, one per line; the paste box takes a copied column |
| `websites`, `websiteScope`, `websiteSource` | Website mode: up to 100 domains or page links; Google Ads keywords for them, or the keywords a domain ranks for |
| `ideasSource`, `maxKeywords`, `maxIdeasPerSeed`, `sortBy` | Ideas mode: Google Ads ideas, related searches, suggestions or autocomplete; the caps and the order (`sortBy` for the Google Ads source: relevance, search volume, competition, bids or CPC) |
| `locations`, `language` | Up to 10 locations; the language for ideas, website keywords and insights |
| `historyMonths`, `dateFrom`, `dateTo` | The month window (default the latest 48 months) |
| `planningMonths`, `planFrom` | The months to plan for (default the next 3) |
| `minSearchVolume`, `includeWords`, `excludeWords` | Filters (whole words, one rule for both lists) |
| `mergeCloseVariants`, `searchPartners`, `includeAdultKeywords` | Google Ads options (`mergeCloseVariants` delivers one keyword of each close variant group in the whole run, across lookups and the memory; it works with your list, Google Ads ideas and Google Ads website keywords, and since 2026-10-20 a run that combines it with related searches, suggestions, autocomplete or ranked keywords is refused, free) |
| `speed` | `slow` (queued, default) or `fast` (live requests) |
| `includeInsights`, `includeAiVolume`, `includeBing` | Add-ons |
| `memoryName`, `refreshMemory` | The 30 day memory and its refresh (a memory holds up to 30,000 keywords and 32 MB of stored figures, about 9,000 keywords with insights; past that, new rows are not delivered and not charged, with a free `memory_full` row) |
| `spendingLimitUsd`, `maxRunMinutes` | Limits |

A location name must be written the way Google Ads writes it; `Zurich` alone is refused with the closest names (`Zurich,Switzerland` is the canton, `Zurich,Zurich,Switzerland` the city). Country names also work in their own languages (Schweiz, Suisse) and as ISO codes (CH, CHE) or locale tags (de-CH).

**Speed.** `slow` sends each lookup through the queue: on 2026-10-18 eight queued lookups were ready in 10 to 21 seconds. `fast` sends live requests instead, which skip the queue; the same day five live requests took 23 to 161 seconds, so slow is the right default and fast is for the days the queue is long. A fast run sends a lookup live only while its spending bound can carry the live price; otherwise that lookup goes through the queue without the Fast lookup fee, and the row says which speed was used (`speedUsed`).

### Example row

A keyword row from a platform run of this Actor (Switzerland, 2026-10-18), with the series, the seasonal index and the planning list shortened:

```json
{
  "type": "keyword",
  "keyword": "winterjacke",
  "dataStatus": "ok",
  "noDataReason": null,
  "searchVolume": 5400,
  "monthlySearches": [
    {
      "month": "2022-10",
      "year": 2022,
      "monthNumber": 10,
      "searches": 8100
    },
    {
      "month": "2022-11",
      "year": 2022,
      "monthNumber": 11,
      "searches": 18100
    },
    {
      "...": "46 more months"
    }
  ],
  "monthsWithData": 48,
  "missingMonths": [],
  "competition": "HIGH",
  "competitionIndex": 100,
  "cpc": 0.99,
  "lowTopOfPageBid": 0.41,
  "highTopOfPageBid": 1.22,
  "currency": "USD",
  "seasonality": {
    "cyclesCovered": 4,
    "seasonalIndex": {
      "January": 0.93,
      "July": 0.11,
      "November": 3.88,
      "December": 1.81,
      "...": "8 more months"
    },
    "monthlyAverages": {
      "November": 21375,
      "June": 338,
      "...": "10 more months"
    },
    "peakMonths": [
      "November",
      "October",
      "December",
      "September"
    ],
    "lowMonths": [
      "June",
      "May",
      "April",
      "July",
      "March",
      "February",
      "August"
    ],
    "peakMonth": "November",
    "lowMonth": "June",
    "strength": 63.33,
    "label": "strongly seasonal",
    "peakConsistency": {
      "years": 3,
      "peakWasTopMonth": 3
    }
  },
  "trend": {
    "latestMonth": "2026-09",
    "latestSearches": 6600,
    "momChangePct": 83.3,
    "yoyChangePct": -19.2,
    "last12MonthsTotal": 60770,
    "previous12MonthsTotal": 75220,
    "last3MonthsVsYearBeforePct": -20.2,
    "qoqChangePct": 880.9,
    "direction": "declining"
  },
  "planning": [
    {
      "month": "2026-11",
      "monthName": "November",
      "historicalAverage": 21375,
      "yearsObserved": 4,
      "seasonalIndex": 3.88,
      "lastYearSearches": 18100,
      "changeFromLatestPct": 223.9,
      "historicalEvidence": "November averaged 21,375 searches a month over 4 years (2022 to 2025), 3.88 times the average month; November 2025 had 18,100; that is +223.9% against September 2026 (6,600), the latest month Google reported.",
      "planningAssumption": "Assumes November 2026 follows the pattern of 2022 to 2025. Past search demand is history, not a forecast of demand or sales."
    }
  ],
  "locationCode": 2756,
  "locationName": "Switzerland",
  "locationType": "Country",
  "countryCode": "CH",
  "language": null,
  "searchPartners": false,
  "includeAdultKeywords": false,
  "dateFrom": "2022-10",
  "dateTo": "2026-09",
  "source": "Google Ads (Keyword Planner data through DataForSEO)",
  "sourceEndpoint": "keywords_data/google_ads/search_volume",
  "dataAsOf": "2026-10-15",
  "retrievedAt": "2026-10-18T18:21:02.757Z",
  "spellCorrectedTo": null,
  "closeVariants": [],
  "inputPositions": [
    1
  ],
  "emptyFields": [],
  "discoveredBy": "your keyword list",
  "isSeed": null,
  "discoveredByPaidSource": false,
  "seedKeywords": null,
  "extraWords": null,
  "extraWordsNote": null,
  "conceptGroups": null,
  "discovery": null,
  "fromMemory": false,
  "memoryDeliveredOn": null,
  "rowKey": "ab72497753414315d87f67f5",
  "lookupId": "r0d9c844daf37aaacb283",
  "speedUsed": "slow"
}
```

### FAQ

#### Do I get exact monthly search volumes or ranges like 1K to 10K?

Exact figures as Google Ads reports them to advertisers, for every month of the window, not the ranges Keyword Planner shows to accounts without ad spend. Google rounds its figures (for example 49,500 or 60,500).

#### How do I get the search volume for each month, not only the average?

Every row carries `monthlySearches` with one entry per month of the window (48 by default), and `MONTHLY_SEARCHES.csv` in the run's key-value store has one line per keyword per month for a spreadsheet.

#### How do I find seasonal keywords and when to prepare for them?

Run your keywords or seeds and read `seasonality.peakMonths` and `seasonality.label` on each row, and the plan rows: each ranks the keywords of the run for one upcoming month by that month's historical average. `changeFromLatestPct` shows how far demand usually rises from the latest month to the planned one.

#### How accurate are the search volumes?

They are Google Ads' own figures (the numbers Keyword Planner shows advertisers), read through DataForSEO, so they carry Google's rounding and its merging of close variants: keywords Google gives the same figures are listed in `closeVariants`. A keyword with too few searches has no data and is a free row.

#### Can I get search volume for a city or region?

Yes. Any Google Ads location works: countries, states, regions, cantons, counties, provinces and cities, up to 10 per run, plus Worldwide. The insights add-on is by country.

#### Why does this Actor run with limited permissions?

Least privilege: it reads only its input and writes its own dataset and key-value store. The 30 day memory is a named key-value store it creates in your account under the name you choose.

### What this Actor does NOT do

- It does not forecast. Seasonality and plan ranks describe past searches; the next season can differ.
- It does not say what a product is. Words found in keywords are demand, never product attributes.
- No current month: Google Ads publishes a month after it ends, usually around the middle of the next month.
- No data for keywords Google has too few searches for, or that it does not report (some restricted topics): those are free rows with `dataStatus` `no_data`.
- Not split by device, and no click or impression forecasts.
- Bing figures only for Australia, Canada, France, Germany, the United Kingdom and the United States (and their regions and cities), in English, French or German.
- No Bing only runs: Bing figures come as an add-on on the Google rows.
- No intent classifier of its own: search intent comes from the Labs database with the insights add-on.
- Insights, related searches, suggestions and ranked keywords come from the Labs database, which covers whole countries in the languages it lists, and leaves out keywords it does not hold.
- No backlinks, domain overviews, competitor domain discovery or competitor traffic reports, no Google Trends index, and no use of your own Google Ads or DataForSEO account.
- No Standby (always on) HTTP mode: every lookup buys a data request that costs the same whatever it finds, so each call would need a fee of its own of about $0.25 or more; the run sync endpoint below gives the same rows in one HTTP response.
- Postal code locations are not offered (Google Ads returns no data for them).

### Use it through the API

The Actor works through the Apify API, as a scheduled job, and from Make, n8n or Zapier. The run sync endpoint returns the rows in the same HTTP response, which makes it a keyword volume API:

```bash
curl -X POST "https://api.apify.com/v2/acts/automation_craft~google-keyword-search-volume/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords": ["winter jacket", "rain jacket"], "locations": ["United States"]}'
```

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('automation_craft/google-keyword-search-volume').call({ keywords: ['winter jacket'], locations: ['Canada'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_TOKEN")
run = client.actor("automation_craft/google-keyword-search-volume").call(run_input={"keywords": ["winter jacket"], "locations": ["Canada"]})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

### Changelog

- 0.1 (2026-10-18): first release.
- 0.1 (2026-10-20): Merge close variants holds across the whole run, the memory and restarts, and is counted before any maximum; with related searches, suggestions, autocomplete or ranked keywords it is now refused, free. Add-ons of large lists reach every row; an add-on the run cannot carry is skipped with a free row that says why and what to do, never a stop of the run. Memory size bound, restart and summary corrections.
- 0.1 (2026-10-21): the summary gains `rowCountsCover`: after a restart that could not read its dataset back, its row counts cover that attempt only, and a note says so; the charged events stay those of the whole run.

### More data tools by Automation Craft

- [Google Trends Scraper - Compare and Trending Now](https://apify.com/automation_craft/google-trends-scraper)
- [Google Ads Transparency Scraper - Decoded Creatives](https://apify.com/automation_craft/google-ads-transparency-scraper)
- [Google Ads Library Scraper - Competitor Ads, No Login](https://apify.com/automation_craft/google-ads-library-scraper)
- [Amazon Sponsored Products Scraper - Ads by Keyword](https://apify.com/automation_craft/amazon-sponsored-products-scraper)
- [Google News Scraper: Search, Topics, Decoded URLs](https://apify.com/automation_craft/google-news-scraper)
- [YouTube Ads Scraper - Video Ads by Advertiser](https://apify.com/automation_craft/youtube-ads-scraper)

# Changelog

This Actor's version history is a separate document: https://apify.com/automation_craft/google-keyword-search-volume/changelog.md

# Actor input Schema

## `mode` (type: `string`):

volume: monthly Google search volume for exactly the keywords you list. ideas: related keyword ideas for your seed keywords, each with its own monthly volume (the seeds are included). website: the keywords Google Ads finds for a domain or a page, or the keywords a domain ranks for.

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

One keyword per line. In the volume mode up to 10,000 keywords; in the ideas mode up to 200 seed keywords. Google Ads accepts up to 80 characters and 10 words per keyword, and no emoji or symbols; a keyword it would refuse is listed in a free status row instead. Example: winter jacket.

## `keywordsText` (type: `string`):

Optional: paste keywords here, one per line (commas also split). They are added after the Keywords list above. Useful for a column copied from a spreadsheet.

## `websites` (type: `array`):

Website mode only: up to 100 domains such as example.com, or with Website scope page, page links such as https://example.com/shoes/. Each website is one lookup.

## `websiteScope` (type: `string`):

Website mode only. site (the default): keywords for the whole domain. page: keywords for one page link.

## `websiteSource` (type: `string`):

Website mode only. google_ads (the default): keywords Google Ads suggests for the site or page. ranked: keywords the domain ranks for in Google search, with its ranking position, page, title and estimated traffic, from the Labs database (adds the Discovered keyword event per charged row).

## `ideasSource` (type: `string`):

Ideas mode only. google_ads (the default): Google Ads keyword ideas, up to 20 seeds a lookup. related: the searches Google shows as related to each seed, two levels deep. suggestions: longer searches that contain the seed. autocomplete: what Google suggests while typing the seed, then the seed followed by each letter. The last three add the Discovered keyword event per charged row; their keywords then get their monthly series from Google Ads.

## `maxKeywords` (type: `integer`):

Ideas and website modes: the most keyword rows with data delivered per location (default 1,000). The volume mode delivers every keyword you list.

## `maxIdeasPerSeed` (type: `integer`):

Ideas mode only, optional: each seed gets its own lookup and at most this many keyword rows with data (with the Google Ads source, one lookup per seed instead of one per 20 seeds, so more Lookup events).

## `locations` (type: `array`):

Up to 10 locations, one per line: a country (Switzerland), a two letter code (CH), a Google Ads location code (2756), Worldwide, or a region or city written the way Google Ads writes it (Zurich,Switzerland for the canton, Zurich,Zurich,Switzerland for the city). A name Google Ads does not know is refused in a free row that lists the closest names. Left out: United States (an empty list is refused).

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

Optional language: a code (de, fr, en), a name (German), a Google Ads language id (1001) or a locale tag (de-CH). Google Ads volumes do not depend on the language, so the volume mode does not send it; it narrows keyword ideas and website keywords, and picks the language of the Labs insights and of autocomplete.

## `historyMonths` (type: `integer`):

How many months of monthly searches, ending with the latest month Google Ads has published (default 48, the most Google Ads keeps). The price does not depend on it. Use either this or Date from.

## `dateFrom` (type: `string`):

Optional first month of the history, for example 2023-01. Google Ads keeps the last 48 months.

## `dateTo` (type: `string`):

Optional last month of the history, for example 2025-12. Default: the latest month Google Ads has published (the current month is never available).

## `planningMonths` (type: `integer`):

How many upcoming months get a planning rank (default 3: the three months after the month the run starts). Each keyword row gets the historical figures for those months, and one plan row per month ranks the keywords of the run.

## `planFrom` (type: `string`):

Optional first planned month, for example 2026-12. Default: the month after the run starts.

## `minSearchVolume` (type: `integer`):

Optional: only keywords whose average monthly searches are at least this many. Keywords below it are left out and not charged.

## `includeWords` (type: `array`):

Optional: keep only keywords that contain at least one of these words or phrases as whole words (for example damen, or rain jacket).

## `excludeWords` (type: `array`):

Optional: leave out keywords that contain any of these words or phrases as whole words (the same matching rule as Must contain words).

## `sortBy` (type: `string`):

Ideas and website modes with the Google Ads source only: the order that decides which ideas fill Maximum keyword ideas (default relevance). CPC reads the whole Google Ads answer and keeps the ideas with the highest CPC.

## `mergeCloseVariants` (type: `boolean`):

Google Ads gives one combined figure to close variants (plurals, word order). A group is keywords with the same average, the same figure in every month and the same words in any order, a word and its regular plural with s, es or ies counting as one. Every row lists the variants of the same lookup that share its figures. With this on, the run delivers and charges one keyword of each group: the first in the order the run fills its rows (your list, keywords from your memory first; for ideas and websites the order of Google's answer, or the CPC order with Order of keyword ideas set to CPC). Every other member, in the same lookup, another lookup of the run or the memory, is left out before any maximum counts it, free; a free row names the positions in your list of members left out after an earlier lookup. It works with your own list, Google Ads keyword ideas and Google Ads website keywords; with related searches, suggestions, autocomplete or ranked keywords the run is refused, free (their candidates are counted before their figures are known).

## `searchPartners` (type: `boolean`):

Add the searches of Google's search partner sites to the volumes (default off: Google search only).

## `includeAdultKeywords` (type: `boolean`):

Let Google Ads return adult keyword ideas (default off).

## `speed` (type: `string`):

slow: lookups go through the queue, usually ready in 10 to 60 seconds. fast: live lookups that skip the queue (on 2026-10-18 they took 23 to 161 seconds, longer than the queue; worth it on days the queue is long); each paid live lookup adds one Fast lookup event.

## `includeInsights` (type: `boolean`):

Optional add-on per charged keyword row: keyword difficulty (0 to 100), search intent, SERP features, Google result count, categories and about 8 years of monthly history from the Labs database, by country. Charged as one Keyword insight event per row that gets it.

## `includeAiVolume` (type: `boolean`):

Optional add-on per charged keyword row: how often the keyword is asked in AI tools, with 12 months, by country. Charged as one AI search volume event per row that gets it.

## `includeBing` (type: `boolean`):

Optional: Bing search volume, CPC, competition and 24 months for the charged keyword rows, where Bing has data (Australia, Canada, France, Germany, the United Kingdom and the United States). Each Bing request that delivers figures is one Lookup event.

## `memoryName` (type: `string`):

Optional name of a memory kept in your account. A keyword, location and month range charged in the last 30 days under this name is delivered again free from the memory, with no new lookup (add-ons come along when they were bought for the same language and country). Use the same name for retries and repeat runs; runs that share a name must not overlap. Up to 30,000 keywords per name and 32 MB of stored figures (about 9,000 keywords with insights); past that, new rows are not delivered and not charged, with a free row that says so.

## `refreshMemory` (type: `boolean`):

With a memory name: look every keyword up again (charged) and store the new figures, instead of serving the stored ones.

## `spendingLimitUsd` (type: `number`):

Optional: the run stops before its charges would pass this amount (the start fee included). The run option Max total charge works the same way; the lower of the two applies.

## `maxRunMinutes` (type: `integer`):

Optional: after this many minutes no new lookup starts; the run delivers what it has and ends with its summary.

## Actor input object example

```json
{
  "mode": "volume",
  "keywords": [
    "winter jacket",
    "rain jacket",
    "swimsuit"
  ],
  "locations": [
    "United States"
  ],
  "speed": "slow"
}
```

# Actor output Schema

## `items` (type: `string`):

No description

## `monthly` (type: `string`):

No description

## `planner` (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 = {
    "keywords": [
        "winter jacket",
        "rain jacket",
        "swimsuit"
    ],
    "locations": [
        "United States"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation_craft/google-keyword-search-volume").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "keywords": [
        "winter jacket",
        "rain jacket",
        "swimsuit",
    ],
    "locations": ["United States"],
}

# Run the Actor and wait for it to finish
run = client.actor("automation_craft/google-keyword-search-volume").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "keywords": [
    "winter jacket",
    "rain jacket",
    "swimsuit"
  ],
  "locations": [
    "United States"
  ]
}' |
apify call automation_craft/google-keyword-search-volume --silent --output-dataset

```

## MCP server setup

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

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/AmPSmgrCSDSNvFWkC/builds/ucq9Ray7U4BxQkkag/openapi.json
