# Google Keyword Search Volume API: 7 Countries + Global (`quarrywise/keyword-search-volume-api`) Actor

Bulk Google keyword search volume API: monthly volume, CPC and advertiser competition for up to 10,000 keywords a run. Global, US, UK, Canada, Australia, New Zealand, India and South Africa only. Keywords with no data return null and are charged; invalid and failed ones are free.

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

## Pricing

from $2.00 / 1,000 keyword results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

## Google Keyword Search Volume API: 7 Countries + Global

**Keyword search volume API for bulk keyword lists: paste up to 10,000 keywords and get monthly
search volume, cost per click (CPC) and advertiser competition for each one, in one table you can
download as JSON, CSV or Excel or read through the API.** The numbers are Google
Keyword Planner data from the Keywords Everywhere API, used with the provider's permission; not
affiliated with Keywords Everywhere or Google.

Markets: **Global, United States, United Kingdom, Canada, Australia, New Zealand, India and
South Africa only.** Other countries are not available from our data source, and this Actor does not
estimate them. There is no language setting.

### What you get for every keyword

- **Search volume:** the source's own 12-month average of monthly searches in the market you
  choose, rounded the way Google Keyword Planner rounds (for example 301,000 rather than 291,500).
  That is what we measured in our live test on 2026-10-26: every volume matched the average of the
  source's 12 monthly values, snapped to Keyword Planner's volume steps. The source does not
  document it. Spikes in those monthly values can raise it (see "Known data issue" below).
- **CPC:** the cost per click advertisers pay, converted into the currency you choose (about 130
  currencies). In our live test the same keyword cost USD 0.47 with `usd` and GBP 0.35 with `gbp`:
  a real conversion, not a relabelled dollar amount. The list is the source's own and still holds
  ten retired currencies, marked "retired" (ANG, BYR, EEK, HRK, MRO, SKK, SLL, STD, VEF and ZMK);
  whether the source still converts them at a current rate is not known (we checked conversion live
  for USD, GBP and AUD only), so use EUR instead of HRK, EEK and SKK.
- **Competition:** the advertiser competition index from 0 (low) to 1 (high), as reported by the
  source. It is about paid search, not SEO ranking difficulty. A 0 may mean the source has no
  competition data for the keyword rather than low competition (in our live test a keyword with
  480 searches a month came back with competition 0 and no bid estimate).
- **Monthly values, on request:** the source's 12 monthly values for this keyword, labelled by the
  source, oldest first, with ISO months (`2026-09`). Off by default (`includeTrend`), same price.
  Read "Known data issue" below before you use them: they looked a month or more older than their
  labels in our check.
- **An honest "no data":** a keyword the source has no data on gets `status: "no_data"` and empty
  (`null`) numbers, never a made-up 0. See "No data is not zero" below.
- **Clean input handling:** pasted blocks are split into lines; case, extra spaces, invisible
  characters and match-type symbols (`"..."`, `[...]`, `+word`) are removed; duplicates are looked
  up and charged once.

Typical uses: keyword research and content planning, sizing a list of topics before writing,
checking CPC before a paid campaign, enriching a spreadsheet or a database of keywords, and giving
an AI agent or an automation real search demand numbers.

### How to use it

1. Paste your keywords, one per line (up to 10,000 lines per run).
2. Pick the market (country), the CPC currency and the data source.
3. Run it. Each keyword becomes one row. The Overview tab shows volume, CPC and competition. The
   "Status and errors" tab shows every row with its status and, for `invalid` and `failed` rows
   (which are free), the reason. Dataset views cannot filter rows, and the API returns every row,
   so to list only the problems, download the dataset as CSV or Excel and filter the `status`
   column for `invalid` and `failed`, or filter `status` in your own code after reading the items.

From code or an automation, call the Apify API, for example
`POST https://api.apify.com/v2/acts/quarrywise~keyword-search-volume-api/run-sync-get-dataset-items`
with your Apify token in the `Authorization: Bearer` header and the input JSON as the body. The
call waits at most 300 seconds; for lists above about 5,000 keywords start the run asynchronously
and read the dataset when it finishes. Apify's apps for Make, Zapier and n8n, and Apify's MCP server
for AI agents (`https://mcp.apify.com?tools=quarrywise/keyword-search-volume-api`), work the same way.

### Input

| Field | What it does | Default |
|---|---|---|
| `keywords` | Keywords, one per line, 1 to 10,000 lines per run (pasted blocks count line by line). Duplicates (ignoring case and spacing) are looked up and charged once | required |
| `country` | `global`, `us`, `uk`, `ca`, `au`, `nz`, `in` or `za`. `global` sends no country filter and returns the source's global data | `us` |
| `currency` | Currency for CPC only, as a lowercase code such as `usd`, `gbp` or `eur`. Search volume is not affected | `usd` |
| `dataSource` | `gkp`: Google Keyword Planner only. `cli`: Google Keyword Planner plus clickstream data. Same price either way | `gkp` |
| `includeTrend` | Adds the source's 12 monthly values for this keyword, labelled by the source. For rough direction only, not timing (see "Known data issue" below). Same price | `false` |

There is no language setting: the source has none, and volume is per market.

Example:

```json
{
    "keywords": ["keyword research", "content marketing", "email marketing"],
    "country": "us",
    "currency": "usd",
    "dataSource": "gkp",
    "includeTrend": false
}
```

### Output fields

One dataset row per unique keyword: the `invalid` rows come first, then every other keyword
(`ok`, `no_data` and `failed` rows) in the order you entered them (the first occurrence counts when
a keyword appears twice). To match rows to your own list, join on `input_keyword` (as you entered
it) or `keyword` (as looked up) rather than on row position. Keywords that were not looked up
because the run reached your maximum charge get no row. After a restart a keyword can appear twice
(see "Aborts, restarts and safety stops" below): keep its `ok` or `no_data` row. Every row has the
same fields.

| Field | Meaning |
|---|---|
| `keyword` | The keyword as looked up: lowercase, spaces collapsed, match-type symbols removed |
| `input_keyword` | The keyword as you entered it (the first spelling when duplicates were merged) |
| `status` | `ok`, `no_data`, `invalid` or `failed` |
| `search_volume` | The source's own 12-month average of monthly searches, rounded as Keyword Planner rounds; spikes in the source's monthly values can raise it (see "Known data issue"); `null` on `no_data`, `invalid` and `failed` rows (an `ok` row always has it) |
| `cpc` | Cost per click in `cpc_currency`, two decimals as the source sends them; `null` when there is no data, when the source sent 0.00 (no bid estimate, see below), or when the value could not be read or trusted; never 0 |
| `cpc_currency` | The currency code you chose, for example `USD`; `null` when `cpc` is `null` |
| `competition` | Advertiser competition index as reported by the source, 0 to 1; 0 may mean the source has no competition data for the keyword, not low competition; `null` when there is no data or the value could not be read |
| `trend` | The source's 12 monthly values for this keyword, labelled by the source: `[{"month": "2026-09", "search_volume": 480}, ...]`, oldest first; for rough direction only, not timing (see "Known data issue"); `null` when not requested (the default) or no data |
| `country` | The market, or `global` |
| `data_source` | `gkp` or `cli` |
| `error_reason`, `error_message` | Why an `invalid` or `failed` keyword has no data; `null` otherwise |
| `retrieved_at` | When the row was produced (ISO 8601, UTC) |

The source returns every keyword exactly as it was sent and in the same order (checked in our
live test); the Actor still matches each answer to its keyword by the keyword itself, and a keyword
it cannot match safely gets a free `not_returned` row instead of someone else's numbers.

Example rows, built from the source's answers in our live test on 2026-10-26 (country `us`, data
source `gkp`, currency `usd`, `includeTrend: true` so the first row shows the source's monthly
values; with the default `false` its `trend` is `null`):

```json
{
    "keyword": "running shoes",
    "input_keyword": "Running Shoes",
    "status": "ok",
    "search_volume": 301000,
    "cpc": 0.47,
    "cpc_currency": "USD",
    "competition": 1,
    "trend": [
        { "month": "2025-10", "search_volume": 550000 },
        { "month": "2025-11", "search_volume": 368000 },
        "... 10 more months, oldest first, up to 2026-09 ..."
    ],
    "country": "us",
    "data_source": "gkp",
    "error_reason": null,
    "error_message": null,
    "retrieved_at": "2026-10-26T17:51:49.000Z"
}
```

```json
{
    "keyword": "quokka sanctuary entrance tickets",
    "input_keyword": "quokka sanctuary entrance tickets",
    "status": "no_data",
    "search_volume": null,
    "cpc": null,
    "cpc_currency": null,
    "competition": null,
    "trend": null,
    "country": "us",
    "data_source": "gkp",
    "error_reason": null,
    "error_message": null,
    "retrieved_at": "2026-10-26T17:51:49.000Z"
}
```

A run summary (keyword counts, charged rows, warnings and the settings used) is saved in the
key-value store record `OUTPUT`, not in the dataset. A run that is not stopped early also says on
the run page what it saved, for example `Done: 3 keyword rows saved (3 charged). Details in the
OUTPUT record.`

### No data is not zero

The source answers a keyword it has no data on with a volume of 0, a CPC of 0.00, a competition of 0
and an empty trend, without a "not found" flag (seen in our live test: the keyword still costs the
source one credit). Showing that as 0 searches would be misleading, so this Actor reads it as
`status: "no_data"` and leaves the numbers empty (`null`). That reading is our interpretation of an
answer with no volume and no monthly searches.

- `no_data`: a volume of 0 where the trend is empty or no month is above 0 (every month 0 or
  without a value), with a CPC or competition value present as in the source's no-data answer.
  All numbers are `null`. A bare volume of 0 with neither is a free `failed` row
  (`unexpected_response`), not `no_data`.
- A volume of 0 **with** at least one month above 0 in the trend is a measured zero (the 12-month
  average rounds to 0): `status: "ok"`, `search_volume: 0`.
- A volume above 0 with an empty trend is `ok` with `trend: []`.
- A missing volume is never shown as 0, and a row without a volume is never charged, not even as
  `no_data`: when the source answers a keyword without a volume (not seen live), whatever its trend
  holds, the keyword gets a free `failed` row (`unexpected_response`). We do not work out a volume
  from the trend ourselves, because the source's rounding is not documented. A month the source sent
  without a value has `search_volume: null` in the trend.
- On `ok` rows the source sends a CPC of 0.00 when it has no bid estimate for the keyword (seen live
  for a keyword with 70 searches a month and a competition of 0.02). That is not a price of 0, so
  this Actor outputs `cpc: null` and `cpc_currency: null` for it (an average over your rows is not
  pulled down), and the run summary counts those rows as `cpc_zero_as_null`.
- When the source sends CPC, competition or the trend in a format we cannot read, or reports the CPC
  with another symbol than the one of the currency you chose, that value is `null`, the row stays
  `ok`, and the run summary says how many rows were affected. That symbol check only works for a
  currency with a symbol of its own: the source writes nine currencies as a bare `$` (ARS, AUD,
  CLP, COP, KYD, MOP, MXN, NZD and USD), so for those it cannot tell them apart. In our live test
  the source did convert: `running shoes` in Australia came back at 0.24 in AUD and 0.17 in USD.

`no_data` rows are charged like `ok` rows: the source bills us for every keyword it answers,
including keywords without data, and "no data" is a real answer about that keyword.

### Known data issue: source data age (checked 2026-10-28)

The source labels its 12 monthly values with the last 12 months (on 2026-10-28: October 2025 to
September 2026), but in our check the values looked older than their labels, by an amount that
differs per keyword and per market:

- **United States and Global: about 1 month late.** Seasonal peaks fall in the month after the
  event: `christmas gifts` peaks in January, `halloween costumes` in November, `valentines day
  gifts` in March and `black friday deals` in December. For 7 US keywords we compared with Google
  Ads data from another provider, every monthly value equalled that data for the month before.
- **Canada, Australia and the United Kingdom: often 2 to 3 months late, for some keywords more.**
  `snow blower` in Canada peaks at June and July 2026 (165,000 each), against 8,100 for October to
  December 2025: a winter keyword peaking in summer, so far more than 3 months off. `mothers day
  gifts` peaks in July to August in Canada and Australia (Mother's Day is in May there) and in June
  in the UK (Mothering Sunday was in March); `valentines day gifts` peaks in April in Canada. For 2
  UK keywords compared with that other data, the values were 2 and 3 months older than their labels
  (a third UK keyword matched no single offset). Some keywords there look only about 1 month late:
  `halloween costumes` in Canada peaks in November, and `christmas gifts` in January in Australia
  and in December and January (tied) in the UK.
- **Shared spikes.** Unrelated commercial keywords carry identical spikes from about April 2026: in
  the US, `crm software`, `project management software` and `black friday deals` all show 550,000
  (June), 246,000 (July), 201,000 (August) and 201,000 (September); in the Global data, four
  software keywords all show 823,000 for June 2026.

What it means for you:

- Use `trend` for rough direction only (for example a steady, rising or seasonal keyword), not for
  timing: do not read the month of a peak from it. That is why it is off by default.
- `search_volume` is the source's own 12-month average, so such spikes can raise it. Global `email
  marketing software` has a `search_volume` of 201,000, while its values for October 2025 to March
  2026 range from 8,100 to 22,200.
- The `cli` data source returned the same values for the keywords we checked.
- This Actor passes the source's numbers through unchanged: it only converts month names to ISO
  months and sorts them oldest first (a comparison of the raw answers with our rows found 0
  differences). Because the offset differs per keyword, we do not try to correct it.

### Pricing

Pay per result: **USD 2.00 per 1,000 keyword rows (USD 0.002 per keyword)**, on every Apify plan,
plus Apify's small start fee per run.

- **Charged:** `ok` and `no_data` rows (keywords the source answered, including "no data").
- **Free:** duplicates (merged before lookup), `invalid` keywords (never sent), and `failed`
  lookups (for example when the source does not answer).
- **Your maximum charge per run is respected:** before every batch the Actor checks how many
  keywords your remaining maximum charge can pay for and looks up only those. When it is used up the
  run ends (it still shows as succeeded) with a status message saying how many keywords got no row,
  the remaining keywords are neither looked up nor charged, and the run summary (`OUTPUT`) shows
  `status: "limit-reached"` and `keywords_not_processed_budget`. The lowest maximum charge you can
  set for this Actor is USD 0.05, which pays for 24 keywords after Apify's start fee.
- **Every Apify plan, the same limits.** A keyword cap for Apify's free plan (100 keywords a run) is
  built but switched off in this version, until we have checked how Apify reports paid plans to a
  run; free-plan runs are not capped. The run log names the plan it detected, for information only
  (`Plan: ...`). If we switch the cap on, the changelog will say so first.

Cost examples, when every keyword is unique, valid and answered (each one a charged row):

| Run | Charged keyword rows | Cost |
|---|---|---|
| The prefilled example (3 keywords, `us`) | 3 | USD 0.006 |
| 1,000 keywords | 1,000 | USD 2.00 |
| 10,000 keywords (the maximum per run) | 10,000 | USD 20.00 |

The costs are for the keyword rows only: Apify's small start fee per run comes on top. Duplicates,
invalid keywords and failed lookups are free, so they make a run cheaper, never dearer. Set your
maximum charge per run above the cost, since the start fee comes off it first: at exactly USD 20.00
a 10,000-keyword run stops at least one keyword short.

### Rows that are free: invalid and failed keywords

| `status` | `error_reason` | When |
|---|---|---|
| `invalid` | `empty` | Nothing is left after removing spaces, invisible characters and match-type symbols |
| `invalid` | `too_long` | Longer than 80 characters (Google Ads keyword limit) |
| `invalid` | `too_many_words` | More than 10 words (Google Ads keyword limit) |
| `invalid` | `no_letters_or_digits` | No letter or digit at all |
| `invalid` | `rejected_by_source` | The source rejected this keyword as invalid |
| `failed` | `rejected_in_batch` | The source rejected a request containing this keyword when the run had reached its limit of 30 rejected requests, so it stopped narrowing down which keyword caused it; try it again in a shorter list |
| `failed` | `not_returned` | The source's answer did not include this keyword, or we could not match it safely (for example an echo that fits two of your keywords) |
| `failed` | `unexpected_response` | The source's answer for this keyword had an unexpected format, or no volume |
| `failed` | `source_error` | The source did not answer (timeout, server error or rate limit), or the run stopped early for a safety reason (see "Aborts, restarts and safety stops" below); `error_message` says which |
| `failed` | `service_unavailable` | The keyword data service is temporarily unavailable for this Actor |

Invalid keywords are filtered out before any lookup: they would come back without data anyway.
When the keyword data service is temporarily unavailable for this Actor, the run ends early with a
message saying how many keywords were not looked up; those are not charged, so you can simply run
them again later. When the source rejects the request settings themselves (the whole list and
three single keywords from it), the run stops with a message that names the country, currency and
data source it used, so you can check them.

### Aborts, restarts and safety stops

- **Abort.** If you abort a run gracefully (the API's `gracefully=true` option), the Actor sends no
  new lookups, waits up to 10 seconds for the lookup already on its way so its rows are written too,
  keeps every row written (charged when it is `ok` or `no_data`) and writes a short run summary. An
  abort that is not graceful (the API default) stops the run at once: rows already written are kept
  and charged, the lookup on its way gets no row and is not charged, and no run summary is written.
  Either way, keywords not looked up get no row and are not charged. Rows answered for later
  keywords while the run is narrowing down a rejected keyword are not written either and not
  charged (the run summary says so).
- **Restarts.** If Apify moves or restarts a run (a migration, a reboot or a crash), the restarted
  run waits about 20 to 25 seconds until the platform's count of charged rows has settled, then
  continues after the last row written, so no keyword is charged twice. A keyword whose row was
  written just before a crash can get a second row: join on `keyword` and keep its `ok` or
  `no_data` row. A free `invalid` or `failed` row can also appear twice after a restart; the copies
  are identical, keep either.
- **When the lookups stop instead.** A crash or kill before the run has charged its first row
  (with 20 or more keywords left to look up), a restart the run cannot verify (its saved place `RESUME_POSITION` is missing, changed or out of
  date, or two restarts came at once), a charged count that does not settle within two minutes, a
  saved place the run could not write, or a set of rows whose write or charge failed: the run then
  charges nothing more for the rows concerned, gives the remaining keywords free `failed` rows, and
  its status message says why. A run metamorphed into this Actor from another Actor looks nothing
  up: start this Actor directly. Known limit: if you resurrect a run that ended before its last
  keyword (for example aborted or timed out) and that was restarted before it ended (a reboot, a
  migration or a metamorph), the resurrected run cannot verify its saved place when Apify kept the
  restart counts from before the end (as it did in our checks), so it looks nothing up and gives
  the keywords left free `failed` rows; start a new run for those. A run that Apify moved before it
  had saved anything (no rows, nothing charged) waits about 60 seconds, then starts from the
  beginning. Please do not edit, delete or put back an older copy of
  `RESUME_POSITION` while a run may still be restarted: an older copy put back by hand can make a
  restart charge some of your keywords a second time, past your maximum charge, if Apify's count of
  charged rows lags by more than about 25 seconds; a deleted copy can do the same after Apify moves
  the run, if both that count and the run's dataset lag by more than about 60 seconds. We measured
  lags of about 5 to 6 seconds.
- **Safety stops.** A run starts small: its first batches hold 20, 20, 40 and 80 keywords before
  reaching 100. It stops looking up keywords, with free rows for the rest, after two failed batches
  in a row, after 30 rejected requests, when more keywords come back without a usable answer (or
  fail in a way that may have used credits) than the larger of 20 and 5% of the keywords sent, or
  when 20 have done so before any row was charged, or after 200 keywords in all came back
  unusable. A request that goes unanswered (a timeout or a
  network error) before the first charge also ends the lookups, because whether it used credits is
  not confirmed; a server error that the source answers, and that the balance shows was not
  billed, does not. The run summary counts such keywords as `unpaid_lookups`.

### Data source, privacy and limits

- **Source:** Keywords Everywhere API, used with the provider's permission; not affiliated with Keywords Everywhere or Google.
  The figures are Google Keyword Planner data (with `cli`, blended with clickstream data).
- **Your keywords:** they are sent to that provider for lookup and are not stored by us after the run.
  There is no cache across runs; each run looks its keywords up fresh. Your keywords are
  returned to you exactly as you entered them (`input_keyword`), so the Actor does not filter them;
  please do not put personal data in a keyword list.
- **Personal data:** the source returns search statistics only, no personal data.
- **Per run:** up to 10,000 keyword lines. Longer lists: split them over several runs.
- **Speed:** keywords are looked up up to 100 at a time (the first batches of a run hold 20, 20, 40
  and 80), and each lookup follows a free balance read: one request to the provider at a time, at
  most one per second in all. In our live test one lookup request (1 to 3 keywords) was answered in
  about 0.2 seconds. A full 100-keyword request has not been timed yet, so 10,000 keywords (about
  103 lookups and as many balance reads, so at least about 3.5 minutes) taking about 4 to 6 minutes
  is still an estimate.
- **Markets:** the seven countries above plus Global, with no language setting. Volume is counted
  per market. The source does answer keywords in other languages (in our live test the French
  keyword `chaussures de running` had 70 searches a month in the United States and 10 in Canada),
  but whether every language spoken in a market is counted is not confirmed, so we do not claim it.

### Provider

Provided by Quarrywise, a trade name of Picazzo Research & Development, Zinkstraat 24, Box C4687, 4823 AD Breda, the Netherlands. KvK 42060545, VAT NL005464235B85. Contact: hello@quarrywise.com or the Issues tab.

# Changelog

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

# Actor input Schema

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

One keyword per line, up to 10,000 lines per run (pasted blocks count line by line). Price: USD 0.002 per keyword row with data or with an explicit no-data answer (USD 2.00 per 1,000 keywords), plus Apify's small start fee per run. Invalid keywords and failed lookups are free. Duplicates (ignoring case and spacing) are looked up and charged once.

## `country` (type: `string`):

Our data source offers only these markets. Other countries (for example Germany, France or Spain) are not available in this Actor, and it does not estimate them. Global sends no country filter and returns the source's global data. There is no language setting.

## `currency` (type: `string`):

Currency for CPC only; search volume is not affected. The list is the data source's own: codes marked retired are retired currencies it still lists, and whether it converts them at a current rate is not known (use EUR instead of HRK, EEK and SKK).

## `dataSource` (type: `string`):

gkp shows data only from Google Keyword Planner. cli shows data from Google Keyword Planner and clickstream data, as Keywords Everywhere describes the two options. Same price either way.

## `includeTrend` (type: `boolean`):

Adds the source's 12 monthly values for this keyword, labelled by the source. Off by default: in our check of 2026-10-28 they looked a month or more older than their month labels (1 to 3 months for most keywords we checked), so use them for rough direction only, not timing (see the README). Same price.

## Actor input object example

```json
{
  "keywords": [
    "keyword research",
    "content marketing",
    "email marketing"
  ],
  "country": "us",
  "currency": "usd",
  "dataSource": "gkp",
  "includeTrend": false
}
```

# Actor output Schema

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

One row per unique keyword: search volume, CPC, competition and status, plus the source's monthly values when requested.

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

Counts of received, merged, invalid, looked-up and charged keywords, warnings and the data source note.

# 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": [
        "keyword research",
        "content marketing",
        "email marketing"
    ],
    "country": "us",
    "currency": "usd",
    "dataSource": "gkp",
    "includeTrend": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("quarrywise/keyword-search-volume-api").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": [
        "keyword research",
        "content marketing",
        "email marketing",
    ],
    "country": "us",
    "currency": "usd",
    "dataSource": "gkp",
    "includeTrend": False,
}

# Run the Actor and wait for it to finish
run = client.actor("quarrywise/keyword-search-volume-api").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": [
    "keyword research",
    "content marketing",
    "email marketing"
  ],
  "country": "us",
  "currency": "usd",
  "dataSource": "gkp",
  "includeTrend": false
}' |
apify call quarrywise/keyword-search-volume-api --silent --output-dataset

```

## MCP server setup

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

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/EBxGMI5p0XcN8sXGe/builds/ZKsHaA802ADvh42j8/openapi.json
