# Keyword Search Volume & CPC Checker: Google Keyword Planner (`hyperbach/keyword-search-volume`) Actor

Keyword search volume checker for a keyword list: Google search volume, CPC, competition, bids, keyword difficulty, search intent and up to 104 months of history per keyword, any country or worldwide. Keyword Planner view, keyword ideas, ranked keywords, AI and Bing search volume. Pay per keyword.

- **URL**: https://apify.com/hyperbach/keyword-search-volume.md
- **Developed by:** [Hyperbach](https://apify.com/hyperbach) (community)
- **Categories:** SEO tools, Developer tools, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.21 / 1,000 keyword rows

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

## Keyword Search Volume & CPC Checker: Google Keyword Planner

**Google keyword search volume, CPC, competition and top-of-page bids for your keyword list — each keyword read on its own, so its figure does not change with the rest of your list, with up to 104 months of history, keyword difficulty, search intent, SERP features and backlink averages on the same row, for any country (and worldwide), 10,000 keywords a run.** Every keyword you give gets a row: a figure, or a typed reason why not (`no_data`, `invalid`, `unsupported_location`, `upstream_error`), and only a row with figures is charged. Google's own Keyword Planner view on request (search partners, cities and postal codes, a date window), with a flag on every keyword whose figure Google merged with its close variants in the same batch — the quiet way other tools return 6,600 for `event tickets` and 720 for `event ticketing` in one run and 6,600 for both in another. Keyword ideas per seed, the keywords a domain ranks for, AI-assistant, Bing and clickstream volumes as add-ons, a per-account memory so the same keyword is never charged twice in the same data month, and `onlyChanges` for a monthly schedule. Save a run as a task and schedule it: **[a monthly tracker that delivers only the keywords that moved](https://apify.com/hyperbach/keyword-search-volume/examples/google-keyword-volume-tracker-monthly-only-keywords-that-moved)**. No login, no Google Ads account, no API key, no proxy.

### Why this keyword volume checker

- **A figure that does not change with your list.** Google's Keyword Planner answers a batch: close variants in one request take one group figure, and which figure depends on the batch (in our test `event ticketing` was 720 alone and 6,600 next to `event ticket` and `event tickets`; 24 of 1,000 buyer keywords took a group figure that differed from their own, by up to 83×). The default view here (`labs`) reads each keyword on its own, so its volume, CPC, competition, bids and history are the same whatever else you list. Close variants that Google counts as one search (plurals, word order, misspellings) share one figure on both views — `yoga mat`, `yoga mats` and `mat for yoga` are 110,000 each, as in 5 of 5 such sets we tried — so do not add them up: the `google_ads` view flags every keyword whose figure is shared with other keywords of the batch (`ads_variants_merged`) and names them (`ads_group_keywords`). `view` = `both` puts the two figures side by side. Ready-made: [Keyword Planner volume next to Labs, close variants flagged](https://apify.com/hyperbach/keyword-search-volume/examples/google-keyword-planner-volume-vs-labs-close-variants-flagged).
- **Every keyword accounted for, every charge explained.** Every keyword you give gets a row with a `status`: `ok` with figures (charged), or free `no_data` (the source holds no figure), `invalid` (over 80 characters or 10 words, a symbol Google Ads refuses — the row says which), `unsupported_location`, `upstream_error` (the source's own error, with the reason) or `not_read` (a cap or the time limit stopped the run before it). `charged` and `billing_note` are on every row; a run that delivers nothing charges nothing.
- **Up to 104 months of history at the base price, difficulty and intent on the same row.** The Labs view carries the monthly series back to 2018 (90–103 months on 73 of 78 ordinary keywords in our test, 18 on the other 5; idea and ranked rows carry 12), the keyword difficulty (0–100), the search intent, the SERP features Google shows (`has_ai_overview` among them), the result count and the backlink averages of the top 10, in one row. Trend columns (`trend_3m_percent`, `trend_12m_percent`, `peak_month`) are computed from the row's own series (the FAQ says how they differ from the source's own `source_trend`).
- **Any country, worldwide and per country in one run.** `locations` takes country codes or names, `worldwide` and numeric Google Ads location codes for cities, regions and postal codes; one row per keyword per location. The Labs view serves 92 countries in their own languages; worldwide, cities and the rest are read on the Google Ads view, and `source_view` says which one each row came from. Ready-made: [search volume by country: US, UK, Canada, Australia and worldwide](https://apify.com/hyperbach/keyword-search-volume/examples/google-search-volume-by-country-us-uk-canada-au-worldwide).
- **Ideas and ranked keywords, filtered before the charge.** `seeds` gives up to 1,000 keyword ideas per seed (topic ideas, long-tail suggestions or Google's related searches), `domains` the keywords a domain ranks for with position, URL and estimated traffic; `minSearchVolume` is applied at the source so a filtered row is never charged. Ready-made: [keyword ideas for a seed, 1,000+ monthly searches](https://apify.com/hyperbach/keyword-search-volume/examples/google-keyword-ideas-for-a-seed-with-1000-plus-searches), [the keywords a website ranks for, top 100 by traffic](https://apify.com/hyperbach/keyword-search-volume/examples/google-keywords-a-website-ranks-for-top-100-by-traffic).
- **A shared cache and a memory that protects your bill.** The source updates its figures once a month (`data_month` on every row); a keyword read earlier in the month can come from the Actor's shared cache with the same figures and its read time (`read_from` says which; when the cache is off the figures are read live), and a keyword you were already delivered in the same data month is not charged again. `onlyChanges` turns a saved task on a monthly schedule into a change feed: only the keywords whose data month or volume moved.
- **Plain HTTPS to a paid API, 256 MB, a few seconds per 700 keywords.** No browser, no page, no proxy; 1,000 keywords are two source tasks (about 30 s with both views), 5,000 keywords took 45 s in our test, 10,000 about two minutes.

### Who it's for

- **SEO and content teams** — a keyword list of any size with volume, difficulty, intent and up to 104 months of seasonality per keyword, in the markets they write for.
- **PPC and agencies** — the Keyword Planner view (competition index, bids, search partners, cities and postal codes) with the variant-merge flag that explains why two tools disagree.
- **Product and market research** — worldwide and per-country rows for the same keywords in one run, trends and peak months, AI-assistant and Bing volumes next to Google's.
- **Developers and agents** — typed rows with stable snake_case names, a status and a charged flag on every row, a run summary to check a pipeline against, and `onlyChanges` for a schedule.

### Quick start

**A keyword list, United States, the Labs view**

```json
{
  "keywords": [
    "event ticketing",
    "sell tickets online"
  ]
}
```

**Both views: the Labs figure next to Google's Keyword Planner figure, close variants flagged**

```json
{
  "keywords": [
    "event ticketing",
    "event ticket",
    "event tickets"
  ],
  "view": "both"
}
```

**Worldwide and two countries in one run**

```json
{
  "keywords": [
    "event ticketing"
  ],
  "locations": [
    "worldwide",
    "US",
    "DE"
  ],
  "language": "en"
}
```

**Keyword ideas for a seed and a domain's ranked keywords, volume 100 and up**

```json
{
  "seeds": [
    "event ticketing"
  ],
  "ideasPerSeed": 100,
  "domains": [
    "eventbrite.com"
  ],
  "keywordsPerDomain": 100,
  "minSearchVolume": 100
}
```

### Output

One row per keyword and location (keyword ideas and ranked keywords are rows of their own kind):

| field | meaning |
|---|---|
| `row_type` | `keyword` (your keyword in one location), `keyword_idea` (an idea of a seed) or `ranked_keyword` (a keyword a domain ranks for). |
| `keyword` | The keyword as the source reads it: lowercase, trimmed, one space between words. Null on a domain's status row. |
| `keyword_inputs` | Every spelling of the keyword you gave (case and spacing variants are folded into one row and charged once). Null on idea and ranked rows. |
| `status` | `ok` (figures served, charged), or free: `no_data` (the source holds no figure for this keyword in this location), `invalid` (no keyword for the source: over 80 characters, over 10 words, a symbol Google Ads refuses; a blank entry of the list is skipped), `unsupported_location` (a location or language the source does not serve, a location code it does not know), `upstream_error` (the source refused or failed the task after the retries), `below_min_volume` (under `minSearchVolume`), `not_read` (the run stopped before it: the cap, the charge limit, the time limit). |
| `status_detail` | Why the row has the status it has, in words: the symbol a keyword contains, the source's own refusal sentence, the floor it fell under, what stopped the run. Null on an ordinary `ok` row. |
| `charged` | `true` when this row is charged (a row with figures that you were not already delivered this data month). Every status row and every repeat inside the data month is `false`. |
| `billing_note` | Why a row with figures is not charged: `delivered to you on <date> for data month <YYYY-MM>; not charged again`. Null otherwise. |
| `task_fee_labs` | On the first charged row of a live Labs or AI task: the number of `labs_task` fees this row carries (1, or 2 with the AI add-on's task). Null on every other row. |
| `task_fee_ads` | On the first charged row of a live Google Ads or Bing task: the number of `ads_task` fees this row carries (1, or 2 with the Bing add-on's task). Null on every other row. |
| `scraped_at` | When the figures were read from the source (UTC, ISO 8601). On a row served from the shared cache, the time of the original read. |
| `location` | The location as you gave it (`US`, `Germany`, `worldwide`, `1023191`). |
| `location_code` | The Google Ads location code the row was read for (2840 = United States, 2276 = Germany, 1023191 = New York). Null for `worldwide`. |
| `location_name` | The location's name from the Actor's table (`United States`, `Germany`, `worldwide`); `location <code>` for a city, region or postal code given as a numeric code. |
| `language_code` | The Google Ads language code the row was read for (`en`, `de`, `es`). |
| `search_partners` | `true` when the figures are for Google Search and its search partners (`searchPartners`, Google Ads view); `false` for Google Search alone. |
| `source_view` | Which view the main figures of this row come from: `labs` (the keyword database: each keyword read on its own, so its figure does not change with the rest of your list; up to 104 months, difficulty, intent) or `google_ads` (Google's Keyword Planner figure as served, close variants merged inside the batch). Close variants that Google counts as one search (plurals, word order, misspellings) share one figure on both views. |
| `data_month` | The source's data month, `YYYY-MM`: the latest month the monthly series holds (Google updates the Keyword Planner data once a month). Figures read inside one data month are the same figures. |
| `source_updated_at` | When the source last refreshed this keyword's record (the Labs view's `last_updated_time`). Null on the Google Ads view. |
| `task_id` | The source's task id of the request that served this row, for tracing a figure. Null on a status row that reached no task. |
| `read_from` | `live` (read from the source in this run) or `cache` (served from the Actor's shared cache: the same figures of the same data month, read at `scraped_at`). |
| `search_volume` | Average monthly searches for the keyword in the location and language (Google's figure). On the `google_ads` view a figure Google merged with close variants in the batch (`ads_variants_merged`). Null on a status row. |
| `competition` | Google Ads competition level: `LOW`, `MEDIUM` or `HIGH` (the Labs view's 0–1 competition read on Google's thresholds). |
| `competition_index` | Competition as an index 0–100 (the Google Ads index as served; on the Labs view its 0–1 competition × 100). |
| `cpc` | Average cost per click in USD, as Google Ads reports it for the keyword. |
| `low_top_of_page_bid` | The lower top-of-page bid estimate in USD (Google Ads). |
| `high_top_of_page_bid` | The higher top-of-page bid estimate in USD (Google Ads). |
| `monthly_searches` | The monthly series, oldest first: `[{"year", "month", "search_volume"}, …]`. Up to 104 months on a Labs keyword row (90–103 on 73 of 78 ordinary keywords in our test, 18 on the other 5), 12 on idea and ranked rows and on the Google Ads view (47 with a date window); trimmed to `dateFrom` / `dateTo` when given. |
| `months_count` | How many months the series holds. |
| `first_month` | The first month of the series, `YYYY-MM`. |
| `last_month` | The last month of the series, `YYYY-MM` (the data month unless `dateTo` cut it). |
| `trend_3m_percent` | The last month's searches against three months earlier, in percent (computed from the row's own series; null when either month is missing or zero). Not the source's `source_trend.quarterly`, which compares with two months earlier. |
| `trend_12m_percent` | The last month's searches against the same month a year earlier, in percent (year over year; computed from the row's own series). |
| `peak_month` | The month with the most searches in the series, `YYYY-MM` (the latest when tied). |
| `source_trend` | The source's own trend object on the Labs view: `{"monthly", "quarterly", "yearly"}` percentage changes against the month before, two months before and a year before (so `quarterly` is not `trend_3m_percent`). Null on the Google Ads view. |
| `keyword_difficulty` | Keyword difficulty 0–100 (the Labs view: the chance of ranking in the top 10, on the source's logarithmic scale). Null on the Google Ads view. |
| `search_intent` | The main search intent: `informational`, `navigational`, `commercial` or `transactional` (Labs view). |
| `secondary_intents` | Further intents the source sees for the keyword (Labs view), or null. |
| `core_keyword` | The main keyword of the synonym group this keyword belongs to, by the source's clustering (Labs view). |
| `detected_language` | The language the source detected for the keyword (Labs view). |
| `categories` | Google product category ids the keyword falls into (Labs view), or null. |
| `serp_item_types` | The kinds of result Google's page shows for the keyword (`organic`, `people_also_ask`, `ai_overview`, `images`, `video`, …; Labs view with `includeSerpInfo`). |
| `serp_results_count` | The number of results Google reports for the keyword (Labs view with `includeSerpInfo`). |
| `serp_check_url` | A Google search URL for the keyword in the location and language (Labs view with `includeSerpInfo`). |
| `has_ai_overview` | `true` when Google's page for the keyword shows an AI Overview (read from `serp_item_types`). |
| `avg_backlinks` | The average number of backlinks of the top-10 organic results (Labs view). |
| `avg_referring_domains` | The average number of referring domains of the top-10 organic results (Labs view). |
| `avg_domain_rank` | The average main-domain rank of the top-10 organic results (the source's scale; Labs view). |
| `spelling_suggestion` | Google Ads' spelling correction of the keyword when it offers one (Google Ads view), or null. |
| `ads_search_volume` | With `view` = `both`: Google's Keyword Planner volume for the keyword as served in this batch (close variants merged), next to the Labs figure in `search_volume`. Null otherwise. |
| `ads_competition` | With `view` = `both`: the Google Ads competition level (`LOW`, `MEDIUM`, `HIGH`). |
| `ads_competition_index` | With `view` = `both`: the Google Ads competition index 0–100. |
| `ads_cpc` | With `view` = `both`: the Google Ads CPC in USD. |
| `ads_low_top_of_page_bid` | With `view` = `both`: the Google Ads lower top-of-page bid in USD. |
| `ads_high_top_of_page_bid` | With `view` = `both`: the Google Ads higher top-of-page bid in USD. |
| `ads_monthly_searches` | With `view` = `both`: the Google Ads monthly series, oldest first (12 months; 47 with a date window). |
| `ads_variants_merged` | On every row read on the Google Ads view: `true` when other keywords of your list in the same batch share this keyword's exact Google Ads figures (volume, competition index, CPC, bids and monthly series): Google merged the close variants into one group figure. `false` when the figure is this keyword's alone within the batch. With `view` = `both`, compare with the Labs figure: the same (as for most plurals and word-order variants) means Google counts these keywords as one search on both views; a different one means the batch changed this keyword's Google Ads figure. |
| `ads_group_keywords` | The other keywords of your list that share this keyword's Google Ads figures (the merged group), or null. |
| `ads_task_id` | With `view` = `both`: the id of the Google Ads task the `ads_*` figures came from (`task_id` is the Labs task of the main figures). |
| `ai_search_volume` | With `includeAiVolume`: how often the keyword is asked of AI assistants per month (the source's AI keyword data), or null. Close variants can share one AI figure (the five `event ticketing` variants all 4,889 in our test; no flag is derived for it); a misspelling has its own, often 0. |
| `ai_monthly_searches` | With `includeAiVolume`: the AI-assistant monthly series, oldest first (12 months). |
| `bing_search_volume` | With `includeBingVolume`: Bing's average monthly searches for the keyword (US, GB, CA, AU, DE, FR). |
| `bing_cpc` | With `includeBingVolume`: Bing's average cost per click in USD. |
| `bing_competition` | With `includeBingVolume`: Bing's competition, 0–1. |
| `bing_monthly_searches` | With `includeBingVolume`: Bing's monthly series, oldest first. |
| `clickstream_search_volume` | With `includeClickstream`: the clickstream-based search volume (Labs view). |
| `clickstream_normalized_volume` | With `includeClickstream`: Google's volume normalized with clickstream data (Labs view). |
| `clickstream_monthly_searches` | With `includeClickstream`: the clickstream monthly series, oldest first. |
| `change_type` | With the per-account memory (any run from your account; the Actor's own runs keep none): `new` (first time this keyword tuple is delivered to you), `changed` (its data month or volume moved since the last delivery) or `unchanged`. Null when the memory was not open. |
| `previous_volume` | The volume as last delivered to you (memory), or null on a new row. |
| `volume_change` | `search_volume` minus `previous_volume`, or null. |
| `volume_history_own` | The Actor's own reads of this keyword tuple for your account: `[{"read_at", "data_month", "search_volume"}, …]`, newest last, up to 24. |
| `seed` | On a `keyword_idea` row: the seed keyword it came from. |
| `idea_source` | On a `keyword_idea` row: `ideas`, `suggestions` or `related`. |
| `domain` | On a `ranked_keyword` row: the domain the keyword ranks for. |
| `rank_absolute` | On a `ranked_keyword` row: the domain's position on Google's page for the keyword, every result counted. |
| `rank_group` | On a `ranked_keyword` row: the position among results of the same kind (organic). |
| `previous_rank_absolute` | On a `ranked_keyword` row: the domain's position at the source's previous check of this keyword, or null (a new ranking). |
| `is_new` | On a `ranked_keyword` row: `true` when the domain did not rank for this keyword at the source's previous check. |
| `is_up` | On a `ranked_keyword` row: `true` when the position rose since the source's previous check. |
| `is_down` | On a `ranked_keyword` row: `true` when the position fell since the source's previous check. |
| `ranked_url` | On a `ranked_keyword` row: the ranking URL. |
| `ranked_title` | On a `ranked_keyword` row: the ranking page's title on Google's page. |
| `estimated_traffic` | On a `ranked_keyword` row: the source's estimated monthly traffic the keyword brings the URL. |
| `is_featured_snippet` | On a `ranked_keyword` row: `true` when the ranking result is a featured snippet. |

Example record:

```json
{
  "row_type": "keyword",
  "keyword": "cat litter",
  "keyword_inputs": [
    "cat litter"
  ],
  "status": "ok",
  "status_detail": null,
  "charged": true,
  "billing_note": null,
  "task_fee_labs": null,
  "task_fee_ads": null,
  "scraped_at": "2026-10-13T11:47:44Z",
  "location": "US",
  "location_code": 2840,
  "location_name": "United States",
  "language_code": "en",
  "search_partners": false,
  "source_view": "labs",
  "data_month": "2026-08",
  "source_updated_at": "2026-09-15 08:11:03 +00:00",
  "task_id": "10131147-1380-0607-0000-96201f17749b",
  "read_from": "live",
  "search_volume": 90500,
  "competition": "HIGH",
  "competition_index": 100,
  "cpc": 2.09,
  "low_top_of_page_bid": 0.98,
  "high_top_of_page_bid": 5.33,
  "monthly_searches": [
    {
      "year": 2025,
      "month": 3,
      "search_volume": 90500
    },
    {
      "year": 2025,
      "month": 4,
      "search_volume": 90500
    },
    {
      "year": 2025,
      "month": 5,
      "search_volume": 90500
    },
    {
      "year": 2025,
      "month": 6,
      "search_volume": 110000
    },
    {
      "year": 2025,
      "month": 7,
      "search_volume": 110000
    },
    {
      "year": 2025,
      "month": 8,
      "search_volume": 110000
    },
    {
      "year": 2025,
      "month": 9,
      "search_volume": 110000
    },
    {
      "year": 2025,
      "month": 10,
      "search_volume": 110000
    },
    {
      "year": 2025,
      "month": 11,
      "search_volume": 110000
    },
    {
      "year": 2025,
      "month": 12,
      "search_volume": 110000
    },
    {
      "year": 2026,
      "month": 1,
      "search_volume": 110000
    },
    {
      "year": 2026,
      "month": 2,
      "search_volume": 90500
    },
    {
      "year": 2026,
      "month": 3,
      "search_volume": 90500
    },
    {
      "year": 2026,
      "month": 4,
      "search_volume": 90500
    },
    {
      "year": 2026,
      "month": 5,
      "search_volume": 90500
    },
    {
      "year": 2026,
      "month": 6,
      "search_volume": 90500
    },
    {
      "year": 2026,
      "month": 7,
      "search_volume": 110000
    },
    {
      "year": 2026,
      "month": 8,
      "search_volume": 110000
    }
  ],
  "months_count": 18,
  "first_month": "2025-03",
  "last_month": "2026-08",
  "trend_3m_percent": 21.5,
  "trend_12m_percent": 0,
  "peak_month": "2026-08",
  "source_trend": {
    "monthly": 0,
    "quarterly": 22,
    "yearly": 0
  },
  "keyword_difficulty": 28,
  "search_intent": "transactional",
  "secondary_intents": null,
  "core_keyword": null,
  "detected_language": "en",
  "categories": [
    10015,
    10130,
    10683,
    11962
  ],
  "serp_item_types": [
    "popular_products",
    "people_also_ask",
    "organic",
    "images",
    "product_considerations",
    "related_searches"
  ],
  "serp_results_count": 222,
  "serp_check_url": "https://www.google.com/search?q=cat%20litter&hl=en&gl=US&ie=UTF-8&uule=w+CAIQIFISCQs2MuSEtepUEUK33kOSuTsc",
  "has_ai_overview": false,
  "avg_backlinks": 318.8,
  "avg_referring_domains": 54,
  "avg_domain_rank": 512.8,
  "spelling_suggestion": null,
  "ads_search_volume": null,
  "ads_competition": null,
  "ads_competition_index": null,
  "ads_cpc": null,
  "ads_low_top_of_page_bid": null,
  "ads_high_top_of_page_bid": null,
  "ads_monthly_searches": null,
  "ads_variants_merged": null,
  "ads_group_keywords": null,
  "ads_task_id": null,
  "ai_search_volume": 50004,
  "ai_monthly_searches": [
    {
      "year": 2025,
      "month": 10,
      "search_volume": 51972
    },
    {
      "year": 2025,
      "month": 11,
      "search_volume": 48829
    },
    {
      "year": 2025,
      "month": 12,
      "search_volume": 53451
    },
    {
      "year": 2026,
      "month": 1,
      "search_volume": 50156
    },
    {
      "year": 2026,
      "month": 2,
      "search_volume": 46135
    },
    {
      "year": 2026,
      "month": 3,
      "search_volume": 47988
    },
    {
      "year": 2026,
      "month": 4,
      "search_volume": 44880
    },
    {
      "year": 2026,
      "month": 5,
      "search_volume": 45041
    },
    {
      "year": 2026,
      "month": 6,
      "search_volume": 36093
    },
    {
      "year": 2026,
      "month": 7,
      "search_volume": 38301
    },
    {
      "year": 2026,
      "month": 8,
      "search_volume": 38556
    },
    {
      "year": 2026,
      "month": 9,
      "search_volume": 50004
    }
  ],
  "bing_search_volume": 36520,
  "bing_cpc": 0.95,
  "bing_competition": 0.9,
  "bing_monthly_searches": [
    {
      "year": 2025,
      "month": 9,
      "search_volume": 39420
    },
    {
      "year": 2025,
      "month": 10,
      "search_volume": 30220
    },
    {
      "year": 2025,
      "month": 11,
      "search_volume": 41120
    },
    {
      "year": 2025,
      "month": 12,
      "search_volume": 42040
    },
    {
      "year": 2026,
      "month": 1,
      "search_volume": 44910
    },
    {
      "year": 2026,
      "month": 2,
      "search_volume": 38370
    },
    {
      "year": 2026,
      "month": 3,
      "search_volume": 38800
    },
    {
      "year": 2026,
      "month": 4,
      "search_volume": 37330
    },
    {
      "year": 2026,
      "month": 5,
      "search_volume": 36630
    },
    {
      "year": 2026,
      "month": 6,
      "search_volume": 28590
    },
    {
      "year": 2026,
      "month": 7,
      "search_volume": 30310
    },
    {
      "year": 2026,
      "month": 8,
      "search_volume": 35650
    },
    {
      "year": 2026,
      "month": 9,
      "search_volume": 31310
    }
  ],
  "clickstream_search_volume": null,
  "clickstream_normalized_volume": null,
  "clickstream_monthly_searches": null,
  "change_type": null,
  "previous_volume": null,
  "volume_change": null,
  "volume_history_own": null,
  "seed": null,
  "idea_source": null,
  "domain": null,
  "rank_absolute": null,
  "rank_group": null,
  "previous_rank_absolute": null,
  "is_new": null,
  "is_up": null,
  "is_down": null,
  "ranked_url": null,
  "ranked_title": null,
  "estimated_traffic": null,
  "is_featured_snippet": null
}
```

### Pricing

**Pay per row with figures, plus one fee per source task the run reads live: no start fee, no minimum, no platform usage billed to you.** Prices fall with your Apify plan.

| per 1,000 | what one event is | Free | Starter | Scale | Business |
|---|---|---|---|---|---|
| `keyword` | a keyword row with figures (`status` ok) | $4.4286 | $3.76431 | $3.10002 | $2.2143 |
| `keyword_idea` | a keyword idea row of a seed | $5.075 | $4.31375 | $3.5525 | $2.5375 |
| `ranked_keyword` | a keyword a domain ranks for | $2.50 | $2.125 | $1.75 | $1.25 |
| `ads_view` | add-on on a keyword row: the Google Ads figures beside the Labs ones (`view` = `both`) | $0.375 | $0.375 | $0.375 | $0.375 |
| `ai_volume` | add-on on a keyword row: AI-assistant search volume (`includeAiVolume`) | $5.00 | $4.25 | $3.50 | $2.50 |
| `bing_volume` | add-on on a keyword row: Bing volume (`includeBingVolume`) | $1.125 | $0.95625 | $0.7875 | $0.5625 |
| `clickstream` | add-on on a keyword row: clickstream volume (`includeClickstream`) | $0.57143 | $0.57143 | $0.57143 | $0.57143 |

The source charges the Actor per task (a batch of up to 700 or 1,000 keywords), so each live task carries one fee, on the first charged row it delivers (`task_fee_labs`, `task_fee_ads` say which row):

| per task | what one event is | Free | Starter | Scale | Business |
|---|---|---|---|---|---|
| `labs_task` | once per live Labs or AI task that delivers a charged row (up to 700 keywords, a seed's ideas, a domain's ranked keywords) | $0.05 | $0.05 | $0.05 | $0.05 |
| `ads_task` | once per live Google Ads or Bing task that delivers a charged row (up to 1,000 keywords) | $0.375 | $0.375 | $0.375 | $0.375 |

A keyword row is **$0.0044286** on the Free plan ($0.0022143 on Business). On the Labs view 1 keyword costs $0.0544286, 100 keywords $0.49286 and 1,000 keywords $4.5286 (two tasks); on the Google Ads view 1,000 keywords cost $4.8036, and with `view` = `both` $5.2786. A figure from the Actor's shared cache (the same keyword read earlier in the same data month) carries no task fee, and a task none of whose rows is charged carries none either. An add-on is charged only on a row that carries its figure. Free: every status row (`no_data`, `invalid`, `unsupported_location`, `upstream_error`, `below_min_volume`, `not_read`), a keyword you were already delivered in the same data month (`billing_note` says when), the unchanged rows `onlyChanges` keeps back, and a run that failed before its first row. A run that stops early (a timeout, an abort) is charged for the rows it delivered. `maxItems` counts the charged rows; before the first request the log says the most the run can cost at your plan's prices. Enterprise plans have their own rates; the Actor's Pricing tab shows the price for your plan.

### Usage patterns

- **Your keywords** — Give `keywords` (a list) or paste them into `keywordsText` (one per line or comma-separated); both are merged, lowercased, trimmed and deduplicated (`keyword_inputs` keeps every spelling you gave), and charged once. Up to 10,000 keywords a run, read in batches of 700 (Labs) or 1,000 (Google Ads). `locations` and `language` apply to every keyword: one row per keyword per location. Ready-made: [search volume, CPC and difficulty for a list of 25](https://apify.com/hyperbach/keyword-search-volume/examples/google-keyword-search-volume-cpc-and-difficulty-for-a-list).
- **Which figure: `labs`, `google_ads` or `both`** — `labs` (the default) is a keyword database built on Google Ads data: each keyword read on its own (its figure does not depend on the rest of your list), up to 104 months, difficulty, intent, SERP features, backlink averages, 92 countries. Close variants that Google counts as one search (plurals, word order, misspellings) share one figure on both views. `google_ads` is Google's Keyword Planner view as Google serves it: 12 months (47 with `dateFrom`), search partners, worldwide, cities and postal codes, and the group figure for close variants, flagged. `both` reads the two and puts the Google Ads figures in the `ads_*` columns of the Labs row (the `ads_view` add-on is charged on each row that carries them). A location or a language the Labs view lacks is read on the Google Ads view by itself; `source_view` says which view a row came from.
- **What a row is** — `row_type` = `keyword`: your keyword in one location, with `status` ok and the figures (charged), or a free status that says why not. `keyword_idea`: one idea of a `seed`, with the Labs figures (12 months of history) and `idea_source`. `ranked_keyword`: one keyword a `domain` ranks for, with `rank_absolute`, `ranked_url`, `ranked_title`, `estimated_traffic`, the position movement and the keyword's figures (12 months). Every row says `source_view`, `data_month`, `source_updated_at`, `task_id` and `read_from` (live or cache).
- **Add-ons and the window** — `includeAiVolume` adds how often the keyword is asked of AI assistants (12 months), `includeBingVolume` Bing's volume, CPC and competition (US, GB, CA, AU, DE, FR), `includeClickstream` clickstream-based and normalized volumes (Labs; doubles the source task). Each is a separate event, charged only on rows that carry a figure. `dateFrom` / `dateTo` (YYYY-MM) trim the monthly series; on the Google Ads view the source serves up to 4 years back. Ready-made: [AI-assistant and Bing search volume next to Google's](https://apify.com/hyperbach/keyword-search-volume/examples/google-keyword-volume-with-ai-assistant-and-bing-search-volume).
- **A schedule, a memory and the cap** — Save a run as a task and schedule it monthly with `onlyChanges`: the Actor keeps a memory per account and `stateStoreName`, delivers only the keywords that are new to it or whose data month or volume moved, and never charges a keyword you were already delivered in the same data month (`billing_note` says when). `maxItems` caps the charged rows; the rows past it get free `not_read` rows that say so. The log's first line says the most the run can cost at your plan's prices. Ready-made: [a monthly tracker that delivers only the keywords that moved](https://apify.com/hyperbach/keyword-search-volume/examples/google-keyword-volume-tracker-monthly-only-keywords-that-moved).

### Input configuration

| field | type | default | what it does |
|---|---|---|---|
| `keywords` | `array` | `[]` | The keyword list, one keyword per entry (`event ticketing`). Up to 10,000 a run; the run reads them in batches of 700 (Labs view) or 1,000 (Google Ads view). Also accepted: `keywordsText` below, merged with this list. |
| `keywordsText` | `string` | `""` | Paste a whole list instead: one keyword per line, or comma-separated. Merged with `keywords`; duplicates are folded and charged once. |
| `locations` | `array` | `[]` | One row per keyword per location. A country as its ISO code (`US`, `DE`, `GB`) or name (`Germany`), `worldwide` (Google's figure for all countries), or a numeric Google Ads location code for a city, region or postal code (`1023191` = New York, `1006886` = London). Empty = United States. Up to 50 locations a run; a location the Actor does not know is a free `unsupported_location` row, never a silent fallback. |
| `language` | `string` | `"en"` | A Google Ads language as its code (`en`, `de`, `es`, `pt`, `ja`, `zh_TW` or `zh-TW`) or name (`German`, `Chinese (Traditional)`). One language a run; the 46 Google Ads languages are accepted. The Labs view serves each country in its own languages (the United States in `en` and `es`, Germany in `de`): a pair the Labs view lacks is read on the Google Ads view, and the row's `source_view` says so. |
| `view` | `labs` / `google_ads` / `both` | `"labs"` | `labs` (the default): a keyword database built on Google Ads data, where every keyword is read on its own (its figure does not depend on the rest of the list): volume, CPC, competition, bids, up to 104 months of history, keyword difficulty, intent, SERP features and backlink averages; 92 countries. `google_ads`: the Google Ads Keyword Planner view as Google serves it, where close variants in one batch take one group figure (`ads_variants_merged` says so), 12 months (47 with a date window), search partners, worldwide, cities and postal codes. `both`: the Labs row with the Google Ads figures in the `ads_*` columns; the `ads_view` add-on is charged on each row that carries them. Each live Labs task (up to 700 keywords) carries one `labs_task` fee and each Google Ads task (up to 1,000) one `ads_task` fee, on its first charged row. Close variants Google counts as one search (plurals, word order, misspellings) share one figure on both views. |
| `searchPartners` | `boolean` | `false` | Google Search and its search partners together (the Keyword Planner's network setting) instead of Google Search alone. A Google Ads setting: with it the keyword rows are read on the `google_ads` view (`event ticketing`: 880 with partners, 720 without, in our test). |
| `dateFrom` | `string` | `""` | The first month of the monthly series, `YYYY-MM` (or a date). On the Google Ads view the source serves up to 4 years back (47 months); on the Labs view the series (up to 104 months) is trimmed to the window. Empty = everything the view holds. |
| `dateTo` | `string` | `""` | The last month of the monthly series, `YYYY-MM` (or a date). Empty = the source's latest month (`data_month` on every row). |
| `includeSerpInfo` | `boolean` | `true` | On the Labs view: the SERP features Google shows for the keyword (`serp_item_types`, `has_ai_overview`), the result count and a check URL. Free. Off = the columns stay null. |
| `includeClickstream` | `boolean` | `false` | Clickstream-based search volume and the clickstream-normalized volume next to Google's, from the `labs` keyword database (`clickstream_search_volume`, `clickstream_normalized_volume`, `clickstream_monthly_searches`). The `clickstream` add-on is charged on each keyword row that carries a figure. |
| `includeAiVolume` | `boolean` | `false` | How often the keyword is asked of AI assistants (ChatGPT and the like), with 12 months of history (`ai_search_volume`, `ai_monthly_searches`; 92 countries). The `ai_volume` add-on is charged on each keyword row that carries a figure, and each AI task (up to 700 keywords) one `labs_task` fee. |
| `includeBingVolume` | `boolean` | `false` | Bing's search volume, CPC, competition and 13 months next to Google's (`bing_*` columns); served for US, GB, CA, AU, DE and FR in English, French and German. The `bing_volume` add-on is charged on each keyword row that carries a figure, and each Bing task (up to 1,000 keywords) one `ads_task` fee. |
| `includeAdultKeywords` | `boolean` | `false` | Ask Google Ads to return figures for adult keywords too (its default hides them). Google may still return no data for some. |
| `minSearchVolume` | `integer` | `0` | Keep only keywords with at least this many monthly searches. Ideas and ranked keywords are filtered at the source before anything is charged; one of your own keywords under the floor is a free `below_min_volume` row without figures. 0 = no floor. |
| `seeds` | `array` | `[]` | Seeds for keyword ideas (`event ticketing`): up to `ideasPerSeed` ideas per seed and location, each a `keyword_idea` row with volume, CPC, competition, difficulty, intent and the seed. Up to 100 seeds a run. |
| `ideasPerSeed` | `integer` | `100` | How many ideas to read per seed and location (1 to 1,000; the source holds up to a million for a broad seed). Applies when `seeds` is given. |
| `ideasSource` | `ideas` / `suggestions` / `related` | `"ideas"` | Which Labs list the ideas come from: `ideas` (the seed's topic, ordered by relevance), `suggestions` (long-tail phrases containing the seed, by volume) or `related` (Google's related searches, two levels deep). |
| `domains` | `array` | `[]` | Domains whose organic ranking keywords you want (`eventbrite.com`, a URL is read as its domain): up to `keywordsPerDomain` keywords per domain and location, the highest estimated traffic first, each a `ranked_keyword` row with the position, the ranking URL and title, the estimated traffic and the keyword's figures. Up to 100 domains a run. |
| `keywordsPerDomain` | `integer` | `100` | How many ranked keywords to read per domain and location (1 to 1,000; eventbrite.com ranks for 1.4 million). Applies when `domains` is given. |
| `onlyChanges` | `boolean` | `false` | For a saved task on a schedule: deliver a keyword row only when it is new to this memory or its data month or volume moved since it was last delivered to you; the unchanged ones are counted in the status message and not delivered. The memory lives with the Actor per Apify account and `stateStoreName`. The source updates once a month, so a monthly schedule is the natural one. |
| `stateStoreName` | `string` | `""` | The name of the memory this run reads and writes (letters, digits, `-`, `_`). The same name across runs tracks the same keyword set; different tasks can keep different names. Empty = one default memory per account. The memory also makes sure a keyword you were already delivered in the same data month is not charged again (`billing_note` says when). |
| `maxItems` | `integer` | `0` | Stop after this many charged rows (keyword rows with figures, idea rows, ranked keyword rows; the free status rows do not count). 0 = no cap. |

### Errors

**A run does not fail because of your input.** When an input cannot be used — a date that is not a date, an id in the wrong form, two settings that contradict each other — the run ends **Succeeded**, charges nothing, and says what to change:

- the status message starts with `INPUT REJECTED`;
- the dataset holds one row, `{"error": true, "code": "…", "message": "…"}`, and no results;
- the key-value store record `ERROR` holds the same object.

From code, check `error` on the first row before reading results.

A keyword the source holds no figures for is not an error: it gets one free `keyword` row with `status` no_data and the reason, and the run goes on with the others. A run that ends **Failed** is a fault on our side, never your input; it charges nothing, and we are alerted.

### FAQ

**Where do the figures come from, and are they Google's?**

From two sources the Actor reads on its own account: the Google Ads Keyword Planner data (the `google_ads` view: the figures Google serves, including its grouping of close variants) and a keyword database built on it (the `labs` view: each keyword read on its own, up to 104 months, difficulty, intent). In our tests the two views agreed on 916 of 1,000 keywords; the 24 that differed were keywords whose Google Ads figure was a group's (108 more shared a group figure that Labs holds too), and the rest had no figure on one side. `source_view`, `data_month`, `source_updated_at` and `task_id` are on every row, so a figure can be traced.

**Why does `event ticketing` show 720 in one tool and 6,600 in another?**

Because Google's Keyword Planner answers a batch, not a keyword: close variants in the same request take one group figure (`event ticketing`, `event ticket`, `event tickets` and `tickets for events` all became 720 in one of our batches, while each is 6,600 or 720 on its own). The `labs` view reads each keyword on its own, so its figure does not move with the batch (`event tickets` 6,600, `event ticketing` 720); close variants that Google counts as one search keep one shared figure there too (`yoga mat` and `yoga mats`, 110,000 each). On the `google_ads` view `ads_variants_merged` and `ads_group_keywords` tell you when and with what a figure was shared.

**Why does a misspelt keyword show the figure of the right spelling?**

Because Google counts a misspelling with its right spelling. On the `labs` view a misspelling the database holds carries the right spelling's figure (`expresso machine` 368,000 like `espresso machine`; `mortgage calculater` like `mortgage calculator`, which `core_keyword` names); one it does not hold is a free `no_data` row. With `view` = `both` or `google_ads` that one is read on the Google Ads view and shows Google's figure, the right spelling's (`projet management software` 165,000 like `project management software`): charged, with `source_view` google_ads and a `status_detail` that says the Labs database has no record. `spelling_suggestion` stays null when the source gives no correction (on every misspelling in our tests).

**What do the trend columns compare?**

`trend_3m_percent` compares the last month with the month three months before it, `trend_12m_percent` with the same month a year before; both are computed from the row's own series. The source's own `source_trend` (Labs view) is not the same: its `monthly` compares with the month before, its `quarterly` with two months before (on 78 of 78 rows in our test) and its `yearly` with a year before. So `trend_3m_percent` and `source_trend.quarterly` differ on most rows (47 of 78; `coffee grinder`: 0 % against 22 %).

**Why is there a fee per source task?**

The source charges the Actor per task, whatever its size (a Labs task of up to 700 keywords, a Google Ads or Bing task of up to 1,000). A run of one keyword would cost the Actor more than its row price, so each live task carries one fee (`labs_task`, `ads_task`) on the first charged row it delivers, and no run is sold below its cost. There is no start fee and no minimum: a keyword served from the shared cache carries no task fee, and a task that delivers no charged row (every keyword `no_data`, say) carries none. The log's first line says the most a run can cost, task fees included; a list whose first row and task fee your maximum charge cannot pay is not read (its free `not_read` rows say what it needs), so no fee is spent on rows you would not receive.

**What is free?**

Every row without figures: `no_data` (the source holds nothing for the keyword in that location), `invalid` (over 80 characters or 10 words, or a symbol Google Ads refuses: ! @ % ^ ( ) = { } ; ~ `< > ? \ | *),`unsupported_location`, `upstream_error`, `below_min_volume`, `not_read`; a keyword you were already delivered in the same data month; the unchanged rows `onlyChanges\` keeps back; and a run that failed before its first row.

**How current are the figures, and how often should I re-run?**

Google updates the Keyword Planner data once a month (the latest month is `data_month` on every row; in our tests the figures held exactly across 26 reads within a month). A monthly schedule with `onlyChanges` is the natural one: a re-run inside the same data month returns the same figures, from the cache, and does not charge you again for them.

**Which countries and languages?**

The `labs` view serves 92 countries in their own languages (the United States in English and Spanish, Germany in German, and so on); the `google_ads` view serves every Google Ads location (217 countries, cities, regions, postal codes, and worldwide) in 46 languages. Give a country as its ISO code or name, a city as its numeric Google Ads location code (`1023191` New York, `1006886` London, `1003854` Berlin, `9040871` Paris). A location the Actor does not know is a free `unsupported_location` row, never a silent fallback to the United States.

**What are the limits?**

10,000 keywords, 50 locations, 100 seeds and 100 domains a run; up to 1,000 ideas per seed and 1,000 ranked keywords per domain. The source allows 12 Google Ads tasks a minute on the account: the Actor paces them and waits out a limit answer at the next minute, so a large list on the `google_ads` view takes about a minute per 12,000 keywords. The `labs` view reads 700 keywords a task, four tasks at a time.

**Can I bring my own API key or Google Ads account?**

No. The Actor runs on its own account and charges per row with figures and per live source task; there is nothing to configure.

**What does a FAILED run mean?**

The source did not serve a single keyword (an outage or a refused credential): every keyword got a free `upstream_error` row with the reason, nothing was charged, and we are alerted. A keyword with no data, a bad keyword or an unknown location never fails a run.

### Integration

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('hyperbach/keyword-search-volume').call({"keywords": ["event ticketing", "sell tickets online"]});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

#### Python

```python
from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('hyperbach/keyword-search-volume').call(run_input={'keywords': ['event ticketing', 'sell tickets online']})
items = client.dataset(run['defaultDatasetId']).list_items().items
```

#### CLI

```bash
apify call hyperbach/keyword-search-volume --input '{"keywords": ["event ticketing", "sell tickets online"]}'
```

#### REST

```bash
curl -X POST "https://api.apify.com/v2/acts/hyperbach~keyword-search-volume/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' -d '{"keywords": ["event ticketing", "sell tickets online"]}'
```

### Support

apify@hyperbach.com

*This page is generated from the Actor's schemas and a live sample — it cannot describe a field the Actor does not have.*

# Actor input Schema

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

The keyword list, one keyword per entry (`event ticketing`). Up to 10,000 a run; the run reads them in batches of 700 (Labs view) or 1,000 (Google Ads view). Also accepted: `keywordsText` below, merged with this list.

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

Paste a whole list instead: one keyword per line, or comma-separated. Merged with `keywords`; duplicates are folded and charged once.

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

One row per keyword per location. A country as its ISO code (`US`, `DE`, `GB`) or name (`Germany`), `worldwide` (Google's figure for all countries), or a numeric Google Ads location code for a city, region or postal code (`1023191` = New York, `1006886` = London). Empty = United States. Up to 50 locations a run; a location the Actor does not know is a free `unsupported_location` row, never a silent fallback.

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

A Google Ads language as its code (`en`, `de`, `es`, `pt`, `ja`, `zh_TW` or `zh-TW`) or name (`German`, `Chinese (Traditional)`). One language a run; the 46 Google Ads languages are accepted. The Labs view serves each country in its own languages (the United States in `en` and `es`, Germany in `de`): a pair the Labs view lacks is read on the Google Ads view, and the row's `source_view` says so.

## `view` (type: `string`):

`labs` (the default): a keyword database built on Google Ads data, where every keyword is read on its own (its figure does not depend on the rest of the list): volume, CPC, competition, bids, up to 104 months of history, keyword difficulty, intent, SERP features and backlink averages; 92 countries. `google_ads`: the Google Ads Keyword Planner view as Google serves it, where close variants in one batch take one group figure (`ads_variants_merged` says so), 12 months (47 with a date window), search partners, worldwide, cities and postal codes. `both`: the Labs row with the Google Ads figures in the `ads_*` columns; the `ads_view` add-on is charged on each row that carries them. Each live Labs task (up to 700 keywords) carries one `labs_task` fee and each Google Ads task (up to 1,000) one `ads_task` fee, on its first charged row. Close variants Google counts as one search (plurals, word order, misspellings) share one figure on both views.

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

Google Search and its search partners together (the Keyword Planner's network setting) instead of Google Search alone. A Google Ads setting: with it the keyword rows are read on the `google_ads` view (`event ticketing`: 880 with partners, 720 without, in our test).

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

The first month of the monthly series, `YYYY-MM` (or a date). On the Google Ads view the source serves up to 4 years back (47 months); on the Labs view the series (up to 104 months) is trimmed to the window. Empty = everything the view holds.

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

The last month of the monthly series, `YYYY-MM` (or a date). Empty = the source's latest month (`data_month` on every row).

## `includeSerpInfo` (type: `boolean`):

On the Labs view: the SERP features Google shows for the keyword (`serp_item_types`, `has_ai_overview`), the result count and a check URL. Free. Off = the columns stay null.

## `includeClickstream` (type: `boolean`):

Clickstream-based search volume and the clickstream-normalized volume next to Google's, from the `labs` keyword database (`clickstream_search_volume`, `clickstream_normalized_volume`, `clickstream_monthly_searches`). The `clickstream` add-on is charged on each keyword row that carries a figure.

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

How often the keyword is asked of AI assistants (ChatGPT and the like), with 12 months of history (`ai_search_volume`, `ai_monthly_searches`; 92 countries). The `ai_volume` add-on is charged on each keyword row that carries a figure, and each AI task (up to 700 keywords) one `labs_task` fee.

## `includeBingVolume` (type: `boolean`):

Bing's search volume, CPC, competition and 13 months next to Google's (`bing_*` columns); served for US, GB, CA, AU, DE and FR in English, French and German. The `bing_volume` add-on is charged on each keyword row that carries a figure, and each Bing task (up to 1,000 keywords) one `ads_task` fee.

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

Ask Google Ads to return figures for adult keywords too (its default hides them). Google may still return no data for some.

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

Keep only keywords with at least this many monthly searches. Ideas and ranked keywords are filtered at the source before anything is charged; one of your own keywords under the floor is a free `below_min_volume` row without figures. 0 = no floor.

## `seeds` (type: `array`):

Seeds for keyword ideas (`event ticketing`): up to `ideasPerSeed` ideas per seed and location, each a `keyword_idea` row with volume, CPC, competition, difficulty, intent and the seed. Up to 100 seeds a run.

## `ideasPerSeed` (type: `integer`):

How many ideas to read per seed and location (1 to 1,000; the source holds up to a million for a broad seed). Applies when `seeds` is given.

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

Which Labs list the ideas come from: `ideas` (the seed's topic, ordered by relevance), `suggestions` (long-tail phrases containing the seed, by volume) or `related` (Google's related searches, two levels deep).

## `domains` (type: `array`):

Domains whose organic ranking keywords you want (`eventbrite.com`, a URL is read as its domain): up to `keywordsPerDomain` keywords per domain and location, the highest estimated traffic first, each a `ranked_keyword` row with the position, the ranking URL and title, the estimated traffic and the keyword's figures. Up to 100 domains a run.

## `keywordsPerDomain` (type: `integer`):

How many ranked keywords to read per domain and location (1 to 1,000; eventbrite.com ranks for 1.4 million). Applies when `domains` is given.

## `onlyChanges` (type: `boolean`):

For a saved task on a schedule: deliver a keyword row only when it is new to this memory or its data month or volume moved since it was last delivered to you; the unchanged ones are counted in the status message and not delivered. The memory lives with the Actor per Apify account and `stateStoreName`. The source updates once a month, so a monthly schedule is the natural one.

## `stateStoreName` (type: `string`):

The name of the memory this run reads and writes (letters, digits, `-`, `_`). The same name across runs tracks the same keyword set; different tasks can keep different names. Empty = one default memory per account. The memory also makes sure a keyword you were already delivered in the same data month is not charged again (`billing_note` says when).

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

Stop after this many charged rows (keyword rows with figures, idea rows, ranked keyword rows; the free status rows do not count). 0 = no cap.

## Actor input object example

```json
{
  "keywords": [
    "event ticketing",
    "sell tickets online"
  ],
  "keywordsText": "",
  "locations": [
    "US"
  ],
  "language": "en",
  "view": "labs",
  "searchPartners": false,
  "dateFrom": "",
  "dateTo": "",
  "includeSerpInfo": true,
  "includeClickstream": false,
  "includeAiVolume": false,
  "includeBingVolume": false,
  "includeAdultKeywords": false,
  "minSearchVolume": 0,
  "seeds": [],
  "ideasPerSeed": 100,
  "ideasSource": "ideas",
  "domains": [],
  "keywordsPerDomain": 100,
  "onlyChanges": false,
  "stateStoreName": "",
  "maxItems": 0
}
```

# Actor output Schema

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

All scraped records in the default dataset. One row per keyword and location (keyword ideas and ranked keywords are rows of their own kind):

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

How the run went: keywords in, invalid, charged; idea and ranked rows charged; free rows by status; rows already billed this data month; unchanged rows kept back (onlyChanges); the data month; the cache's hits and misses; the source's tasks, refusals, rate-limit waits and retries; notes and problems.

# 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": [
        "event ticketing",
        "sell tickets online"
    ],
    "locations": [
        "US"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("hyperbach/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": [
        "event ticketing",
        "sell tickets online",
    ],
    "locations": ["US"],
}

# Run the Actor and wait for it to finish
run = client.actor("hyperbach/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": [
    "event ticketing",
    "sell tickets online"
  ],
  "locations": [
    "US"
  ]
}' |
apify call hyperbach/keyword-search-volume --silent --output-dataset

```

## MCP server setup

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