# Google Ads Keyword Search Volume API - Keyword Planner, CPC (`countonce/keyword-search-volume-api`) Actor

Google Keyword Planner search volume, CPC, competition and 12-month trend for up to 1,000 keywords, any country, state or city. No Google Ads account. Close variants Google counts together are grouped, so totals are not double-counted. $0.25 per request + $3.50 per 1,000 keywords.

- **URL**: https://apify.com/countonce/keyword-search-volume-api.md
- **Developed by:** [CountOnce Data](https://apify.com/countonce) (community)
- **Features:** Limited permissions, Single-tenant, Server interface
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 keyword with data

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Ads Keyword Search Volume API - Keyword Planner, CPC

Search volume, CPC, competition and a 12-month trend from Google Ads Keyword Planner for up to 1,000 keywords at once, for any country, state or city. **No Google Ads account, no API key.** One input field: paste your keywords and press Start.

### Why this one

- **Totals you can trust.** Google gives close variants one shared figure. Other tools either repeat it on every variant or drop your keywords. Here every keyword you sent gets its own row, variants are marked as one group, and you get a **deduplicated total**.
- **Pay only for real data.** Keywords with no data, invalid keywords, merged variants and anything you already fetched in the last 30 days are **free**.
- **Instant API.** Call one URL, get JSON back in the same request. Works with n8n, Make, Zapier, your own code and AI agents (MCP).
- **Planning fields included.** Peak month, seasonality index, monthly and quarterly trend, top-of-page bid range.
- **Bill cap.** Set a maximum cost per run and the Actor will never go over it.

**The one thing other keyword tools leave to you:** Google reports one combined figure for close variants ("running shoes", "running shoe", "shoes running", "runing shoes"). Paste all four into a typical tool and you either get four rows of 246,000 that add up to 984,000 searches that do not exist, or three of your keywords silently vanish. This Actor returns **one row for every keyword you sent**, marks the variants as one group, and gives you a **deduplicated total** you can put in a report.

#### What deduplication looks like (real run, United States)

| Keyword | Searches / month | Variant group | Counts toward total |
| --- | --- | --- | --- |
| standing desk | 135,000 | g1 | yes |
| standing desks | 135,000 | g1 | no (same searches as above) |
| standing desk converter | 18,100 | - | yes |
| **Total** | **153,100** (a naive sum says 288,100) | | |

**Price:** $0.25 per live request (up to 1,000 keywords) + $0.0035 per keyword with data. 200 keywords cost $0.95.

### Instant API (Standby)

Send keywords to one URL and get the rows back in the same response, with no run to start and nothing to poll. Use your Apify API token.

```bash
curl "https://countonce--keyword-search-volume-api.apify.actor/?keywords=standing%20desk,standing%20desks&location=United%20States" \
  -H "Authorization: Bearer <APIFY_TOKEN>"
```

For longer lists, POST the same JSON you would use as the Actor input:

```bash
curl -X POST "https://countonce--keyword-search-volume-api.apify.actor/" \
  -H "Authorization: Bearer <APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"keywords": ["running shoes", "trail running shoes"], "location": "Germany", "language": "de"}'
```

The response is `{ "summary": {...}, "rows": [...] }`. The first call after a quiet period takes a few extra seconds to start; calls that follow are answered straight away. Pricing is the same as for normal runs.

### What you get per keyword

| Field | Meaning |
| --- | --- |
| `keyword` | Your keyword, normalized (lower case, extra symbols removed) |
| `searchVolume` | Average monthly searches on Google, last 12 months |
| `cpc`, `lowTopOfPageBid`, `highTopOfPageBid` | Advertiser cost per click and bid range, USD |
| `competition`, `competitionIndex` | LOW / MEDIUM / HIGH and 0-100 (advertiser competition, not SEO difficulty) |
| `monthlySearches` | Month-by-month searches, last 12 months |
| `trendMonthlyPct`, `trendQuarterlyPct` | Change of the latest month against the month before and against three months before, in percent |
| `peakMonth`, `seasonalityIndex`, `seasonal` | The strongest month, how far it stands above the 12-month average (2.0 = double), and a true/false flag |
| `variantGroup`, `variantGroupSize` | Keywords Google counts together share a group id |
| `countsTowardTotal` | `true` for one keyword per group. Sum `searchVolume` over these rows only |
| `mergedInto` | Set when Google returned no separate row for your keyword because it was merged into another one you sent |
| `status` | `OK`, `MERGED_BY_GOOGLE`, `NO_DATA` or `INVALID_KEYWORD` |
| `source` | `live`, `cache` (your own 30-day cache, free) or `demo` |

Every run also stores a `SUMMARY` record: rows delivered, rows from cache, `totalVolumeNaive`, `totalVolumeDeduplicated`, and the amount charged.

### Input

```json
{
  "keywords": ["running shoes", "running shoe", "trail running shoes"],
  "location": "United States",
  "language": "en"
}
```

Only `keywords` is required. `keywords` also accepts one comma- or line-separated string.

- **Country, state or city:** `"Germany"`, `"Texas,United States"`, `"London,England,United Kingdom"`, or a numeric Google Ads location code.
- **Limits Google sets:** 80 characters and 10 words per keyword. Anything longer comes back as `INVALID_KEYWORD` with the reason, at no charge, and the rest of the batch still runs.

### Sample output

```json
{
  "keyword": "running shoe",
  "searchVolume": 246000,
  "cpc": 1.17,
  "competition": "HIGH",
  "competitionIndex": 99,
  "trendMonthlyPct": 22,
  "trendQuarterlyPct": 22,
  "peakMonth": "November",
  "seasonalityIndex": 1.16,
  "seasonal": false,
  "variantGroup": "g1",
  "variantGroupSize": 2,
  "countsTowardTotal": false,
  "status": "OK",
  "source": "live"
}
```

### Pricing

| Event | Price | When |
| --- | --- | --- |
| Live request | $0.25 | Once per run that fetches new data, for up to 1,000 keywords |
| Keyword with data | $0.0035 | Per keyword returned with search volume |

| Keywords in one run | You pay |
| --- | --- |
| 20 | $0.32 |
| 200 | $0.95 |
| 1,000 | $3.75 |

**Never charged:** keywords with no data, invalid keywords, keywords merged into another keyword of the same run, and any keyword your account already fetched for the same market in the last 30 days. A run answered fully from your cache costs nothing.

**Cap the bill:** set *Maximum cost per run* in the run options. The Actor checks the limit before it fetches anything. If the limit is reached mid-run, the remaining rows wait in your cache and the next run delivers them for the row fee only.

**If the data source fails,** nothing is charged. You are charged only after the rows are saved in your dataset.

**Free Apify plan:** sample rows only (marked `source: "demo"`), nothing charged. Any paid Apify plan unlocks live data.

### Run-based API (n8n, Make, your own code)

If you prefer classic runs: one HTTP call starts a run and returns the rows when it finishes: about 10 seconds for new data, 1-2 seconds when everything comes from your cache.

```bash
curl -X POST "https://api.apify.com/v2/acts/countonce~keyword-search-volume-api/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"keywords": ["running shoes", "trail running shoes"], "location": "United States"}'
```

The response is the JSON array of rows. In n8n or Make, use the Apify node, or an HTTP Request node with the same URL.

### Use it from an AI agent (MCP)

Add this Actor to the Apify MCP server (`https://mcp.apify.com/?tools=countonce/keyword-search-volume-api`). The agent needs one argument, `keywords`. Tell it to sum `searchVolume` only over rows where `countsTowardTotal` is true.

### Track demand every month

Google refreshes these figures once a month. Put the same input on an Apify Schedule monthly; the cache expires after 30 days, so each scheduled run fetches the new month.

### FAQ

**Why do several of my keywords have the same number?** Google Ads groups close variants (plurals, word order, misspellings) and publishes one figure for the group. They share a `variantGroup` here; count the group once.

**Is the group detection exact?** Google does not publish its grouping. Two keywords are put in one group only when their volume and all twelve monthly values are identical *and* the wording is close (same words in another order, plural, or a one- or two-letter difference). Unrelated keywords that happen to share numbers are left apart.

**How precise are the volumes?** They are Google's own rounded figures (for example 246,000 or 301,000), not exact counts.

**Why does a keyword have no data?** Google publishes nothing for very rare or new phrases, and for some sensitive topics. Those rows are free.

**Is `competition` the same as SEO difficulty?** No. It describes how many advertisers bid on the keyword.

**Where does the data come from?** Google Ads Keyword Planner data, supplied by DataForSEO.

### Support

Open an issue on the Issues tab with your run ID. Answered within two working days.

# Actor input Schema

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

Up to 1,000 keywords per run. One per line. Duplicates are removed; keywords Google Ads cannot accept (over 80 characters, over 10 words, symbols only) are returned as INVALID_KEYWORD and never charged.

## `location` (type: `string`):

Country name ("Germany"), Google Ads location name ("London,England,United Kingdom", "Texas,United States") or numeric Google Ads location code.

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

Two-letter language code: en, de, es, fr ...

## `includeMonthlySearches` (type: `boolean`):

Month-by-month searches for the last 12 months. Trend and seasonality fields are returned either way.

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

Off = Google Search only (the usual choice for SEO).

## Actor input object example

```json
{
  "keywords": [
    "running shoes",
    "running shoe",
    "trail running shoes"
  ],
  "location": "United States",
  "language": "en",
  "includeMonthlySearches": true,
  "searchPartners": false
}
```

# Actor output Schema

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

No description

# API

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

## JavaScript example

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

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

// Prepare Actor input
const input = {
    "keywords": [
        "running shoes",
        "running shoe",
        "trail running shoes"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("countonce/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": [
        "running shoes",
        "running shoe",
        "trail running shoes",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("countonce/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": [
    "running shoes",
    "running shoe",
    "trail running shoes"
  ]
}' |
apify call countonce/keyword-search-volume-api --silent --output-dataset

```

## MCP server setup

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