# Bulk Keyword Search Volume & CPC - Keyword Planner (`ocius-data/keyword-metrics`) Actor

Bulk Google Keyword Planner data with no Ads account: volume, CPC, competition, 12-month trend. From USD 0.20 per batch of up to 1,000 keywords.

- **URL**: https://apify.com/ocius-data/keyword-metrics.md
- **Developed by:** [Christian Barbosa](https://apify.com/ocius-data) (community)
- **Categories:** SEO tools, Agents
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $200.00 / 1,000 keyword batch with data

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

## Bulk Keyword Search Volume & CPC - Keyword Planner

Get **Google Ads search volume, cost per click (CPC), competition, top-of-page bids and a monthly trend** for up to 20,000 keywords per run, in any country, region or city and in any language. Optionally add **Bing Ads** data and **related keyword ideas**. Pricing is a small fee per batch of up to 1,000 keywords plus a fraction of a cent per keyword with data: keywords without data, invalid keywords and errors carry no per-keyword fee.

The data is the same data that powers the Google Ads Keyword Planner, delivered through the DataForSEO Keywords Data API. You do not need a Google Ads account, a developer token or an active ad campaign.

### Contents

- What this Actor does
- Who uses it
- How to use it
- Input
- Output and field dictionary
- Pricing and cost calculator
- Plan requirements
- Statuses, errors and the run summary
- Integrations: schedule, Google Sheets, webhooks, Zapier, Make and n8n
- API usage
- Known limits
- FAQ
- Changelog

### What this Actor does

- **Monthly search volume** for each keyword: the average number of searches per month in the selected location and language.
- **CPC**: the average cost per click advertisers paid, in USD.
- **Competition** as a label (`LOW`, `MEDIUM`, `HIGH`) and as an index from 0 to 100.
- **Top-of-page bids** (low and high range): what advertisers bid to appear at the top of the results page.
- **Monthly trend**: searches for each month in the date range (the past 12 months by default, up to 4 years back), plus a ready-made **3-month** trend percentage (and **year-over-year** change when the date range covers at least 13 months).
- **Bing Ads** search volume, CPC and competition (optional).
- **Related keywords** with the same metrics, generated from your first 20 keywords (optional, billed separately).

#### Why this Actor

- **Fair pricing for small and large lists.** A small fee per batch of up to 1,000 keywords plus a fraction of a cent per keyword. A 50-keyword check costs about USD 0.21; a full batch of 1,000 costs USD 0.45.
- **You never pay for empty results.** Keywords that Google reports without data are listed in the run summary and are not charged.
- **Your cost limit is respected before any data is requested.** If you set a maximum cost per run, the Actor sends only the keywords that the limit can pay for and reports the rest as not processed.
- **Clear status for every keyword.** Each keyword ends as `OK`, `NO_DATA`, `INVALID_INPUT`, `ERROR` or `NOT_PROCESSED_*`, with the reason. If more than 20% of keywords fail, the run is marked as failed so you notice.
- **Safe on long runs.** Progress is saved after every batch, so a platform restart never requests the same data twice, duplicates rows or charges twice.
- **Clean output.** The dataset contains only keyword data. Statuses and errors live in the `RUN_SUMMARY` record, never mixed with your data.

### Who uses it

- **SEO specialists and content teams** prioritize topics by real search demand and track seasonality.
- **PPC and paid media managers** estimate budgets with CPC and top-of-page bid ranges before launching campaigns.
- **Agencies** run keyword research for many clients and countries in one scheduled job.
- **Marketplace and e-commerce teams** measure demand for products and categories by country.
- **Developers and data teams** enrich keyword lists in pipelines through the Apify API, n8n, Make or Zapier.

### How to use it

1. Paste your **keywords**, one per line.
2. Set the **location**: a country, region or city name such as `United States`, `Colombia` or `London,England,United Kingdom`, or a location code such as `2840`. Leave it empty for worldwide data.
3. Set the **language**, for example `English`, `Spanish` or the code `es`.
4. Optional: choose **Bing Ads**, set a **trend date range**, or enable **related keywords**.
5. Click **Start**. A batch of 1,000 keywords is a single request to the source, which answers in about 7 seconds on average.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `keywords` | array of strings | required | Up to 20,000 keywords per run. Duplicates are removed (case-insensitive). Google Ads accepts up to 80 characters and 10 words per keyword; Bing up to 100 characters. Emojis are not accepted. |
| `location` | string | `United States` | Location name or DataForSEO location code. Empty means worldwide (Google only). |
| `language` | string | `English` | Language name or code. Bing supports English, French and German. |
| `searchEngine` | `google` or `bing` | `google` | Source of the metrics. |
| `includeMonthlySearches` | boolean | `true` | Adds the `monthlySearches` array. |
| `dateFrom` | string (YYYY-MM-DD) | empty | First month of the trend. Up to 4 years back (Google) or 2 years (Bing). |
| `dateTo` | string (YYYY-MM-DD) | empty | Last month of the trend. Must be a past month. |
| `includeSuggestions` | boolean | `false` | Adds related keyword ideas from Google Ads. |
| `maxSuggestions` | integer | `200` | Maximum related keywords, highest search volume first. |
| `searchPartners` | boolean | `false` | Includes Google search partner sites in the metrics. |

Example input:

```json
{
  "keywords": ["air conditioner", "heat pump", "mini split"],
  "location": "United States",
  "language": "English",
  "searchEngine": "google",
  "includeMonthlySearches": true,
  "includeSuggestions": true,
  "maxSuggestions": 100
}
```

Input is validated before any data is requested. A wrong date format, an unsupported Bing language or an empty keyword list stops the run within seconds with a message that explains how to fix it, and nothing is charged.

### Output and field dictionary

Each row is one keyword. Download the results as JSON, CSV, Excel, XML or HTML, or read them through the API. The Output tab has two views: **Keyword metrics** (a flat table) and **Monthly trend**.

Example row:

```json
{
  "type": "keyword",
  "keyword": "heat pump",
  "location": "United States",
  "locationCode": 2840,
  "language": "English",
  "languageCode": "en",
  "searchEngine": "google_ads",
  "searchVolume": 165000,
  "cpc": 6.42,
  "competition": "HIGH",
  "competitionIndex": 87,
  "lowTopOfPageBid": 2.18,
  "highTopOfPageBid": 11.35,
  "trend3MonthsPercent": -12.5,
  "trendYearOverYearPercent": 8.2,
  "monthlySearches": [
    { "year": 2025, "month": 11, "date": "2025-11", "searchVolume": 135000 },
    { "year": 2025, "month": 12, "date": "2025-12", "searchVolume": 165000 }
  ],
  "searchPartners": false,
  "spellCorrection": null,
  "dateFrom": null,
  "dateTo": null,
  "seedKeywords": null,
  "scrapedAt": "2026-11-01T15:04:05.000Z",
  "source": "Google Ads (Keyword Planner data) via DataForSEO"
}
```

The values above illustrate the format only.

| Field | Type | Meaning | When it is null |
|---|---|---|---|
| `type` | string | `keyword` for your keywords, `suggestion` for related keyword ideas. | Never |
| `keyword` | string | The keyword, in lowercase as the source returns it. | Never |
| `location` | string | The location you requested, as written in the input. `Worldwide` if empty. | Never |
| `locationCode` | integer | Location code reported by the source. | Worldwide requests |
| `language` | string | The language you requested. | Never |
| `languageCode` | string | Language code reported by the source. | When not reported |
| `searchEngine` | string | `google_ads` or `bing_ads`. | Never |
| `searchVolume` | integer | Average monthly searches in the date range. Rows without volume are not saved and not charged. | Never |
| `cpc` | number | Average cost per click in USD. | When advertisers did not bid on the keyword |
| `competition` | string | `LOW`, `MEDIUM` or `HIGH` paid-search competition. For Bing, mapped from 0.1, 0.5 and 0.9. | When not reported |
| `competitionIndex` | integer | Competition from 0 to 100 (Google only). | Bing, or when not reported |
| `lowTopOfPageBid` | number | Lower range of top-of-page bids in USD (Google only). | Bing, or when not reported |
| `highTopOfPageBid` | number | Upper range of top-of-page bids in USD (Google only). | Bing, or when not reported |
| `trend3MonthsPercent` | number | Change between the last month and three months earlier, in percent. | Fewer than 4 months, or a zero base |
| `trendYearOverYearPercent` | number | Change between the last month and the same month a year earlier, in percent. | Fewer than 13 months, or a zero base |
| `monthlySearches` | array | Searches per month (`year`, `month`, `date` as YYYY-MM, `searchVolume`), oldest first. | Omitted when the monthly trend is disabled |
| `searchPartners` | boolean | Whether search partner sites are included. | Never |
| `spellCorrection` | string | Spelling correction suggested by Google Ads. | No correction |
| `dateFrom`, `dateTo` | string | The trend range you requested. | Default range |
| `seedKeywords` | array | For related keywords: the seed keywords used. | Your own keywords |
| `scrapedAt` | string | When the data was collected, ISO 8601 in UTC. | Never |
| `source` | string | Data source. | Never |

Google Ads groups close variants (for example singular and plural) and may report the same volume for them. This is how the Keyword Planner works, not a data error.

### Pricing and cost calculator

This Actor uses pay-per-event pricing. You pay for results, not for platform usage.

| Event | Price | When it is charged |
|---|---|---|
| `batch` | USD 0.20 | Once per batch of up to 1,000 keywords that returns data (also once for the related keywords request). |
| `keyword` | USD 0.00025 | Per keyword saved with data (USD 0.25 per 1,000). |
| `suggestion` | USD 0.00025 | Per related keyword saved (USD 0.25 per 1,000). |

Nothing is charged for keywords without data, invalid keywords, errors or runs stopped by validation.

| Run | Batches | Keywords with data | Related keywords | Cost |
|---|---|---|---|---|
| Quick check | 1 | 50 | 0 | USD 0.21 |
| Content plan | 1 | 500 | 0 | USD 0.33 |
| Full batch | 1 | 1,000 | 0 | USD 0.45 |
| Agency list | 5 | 5,000 | 0 | USD 2.25 |
| Research with ideas | 2 | 200 | 1,000 | USD 0.70 |
| Large list | 20 | 20,000 | 0 | USD 9.00 |

Formula: `cost = 0.20 × batches + 0.00025 × keywords with data + 0.00025 × related keywords`.

**Maximum cost per run.** Set "Maximum cost per run" in the run options to cap your spending. Before each batch, the Actor calculates how many keywords the remaining limit can pay for and sends only those. The rest are reported as `NOT_PROCESSED_CHARGE_LIMIT` in the run summary.

**Defects.** If you believe you received incorrect data, open an issue with the run ID. Every case is reviewed and confirmed defects are fixed with priority.

### Plan requirements

Every request to the data source has a real cost, so this Actor runs on **paid Apify plans** only. On the free plan the run stops immediately with an upgrade message, and nothing is charged. Upgrade at https://apify.com/pricing.

### Statuses, errors and the run summary

Every run writes a `RUN_SUMMARY` record in the default key-value store with the status of each keyword, the counts and a billing reconciliation (charged events compared with saved rows). The run status message shows the same counts.

| Status | Meaning | Charged |
|---|---|---|
| `OK` | Data saved to the dataset. | Yes |
| `NO_DATA` | The source has no search volume for this keyword in this location and language. | No |
| `INVALID_INPUT` | The keyword is too long, has too many words or contains emojis. The reason is included. | No |
| `ERROR` | The source failed for this keyword after retries. | No |
| `NOT_PROCESSED_CHARGE_LIMIT` | Your maximum cost per run was reached. | No |
| `NOT_PROCESSED_FREE_PLAN` | The run was started from a free Apify plan. | No |
| `NOT_PROCESSED_CAPACITY` | The data service was temporarily unavailable. | No |
| `NOT_PROCESSED_RUN_LIMIT` | More than 20,000 keywords in one run. | No |
| `NOT_PROCESSED_STOPPED` | Not sent because an earlier batch was rejected or had almost no data (for example, a wrong location). | No |

| Message | What to do |
|---|---|
| "Invalid input: ..." | Fix the field named in the message and run again. |
| "The data source rejected the request" | Check the location name or code and the language. Location names must match the official list, for example `London,England,United Kingdom`. |
| "... available on paid Apify plans only" | Upgrade to a paid Apify plan. |
| "Only N of M keywords in the first batch had search volume" | Check the location, language and keywords; the remaining batches were not sent to protect your budget. |
| "The keyword data service is temporarily unavailable" | Try again later. You were not charged. If it persists, open an issue. |
| "More than 20% of keywords failed" | Open an issue with the run ID. Keywords with data were saved and charged; failed ones were not. |

### Integrations

#### Schedule recurring checks

Open the Actor, click **Schedule**, and choose a frequency (for example, every Monday at 8:00). Use the same input every time to track how search volume and CPC change over months.

#### Send results to Google Sheets

Use the Apify integration for Google Sheets (Actor run → Integrations → Google Sheets), or import the dataset's CSV link directly with `=IMPORTDATA("https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=csv&view=overview")`.

#### Webhooks

Add a webhook in the Integrations tab with the event "Run succeeded" to notify your system when new data is ready. The payload includes the dataset ID.

#### Zapier, Make and n8n

Use the official Apify apps for Zapier and Make, or the Apify node in n8n: start this Actor with your keywords, wait for the run to finish, and map the dataset rows to your CRM, spreadsheet or database.

### API usage

Start a run and get the results in one request:

```bash
curl -X POST "https://api.apify.com/v2/acts/<USERNAME>~keyword-metrics/run-sync-get-dataset-items?token=<YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"keywords":["air conditioner","heat pump"],"location":"United States","language":"English"}'
```

JavaScript:

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('<USERNAME>/keyword-metrics').call({ keywords: ['heat pump'], location: 'Colombia', language: 'Spanish' });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Python:

```python
from apify_client import ApifyClient
client = ApifyClient("<YOUR_TOKEN>")
run = client.actor("<USERNAME>/keyword-metrics").call(run_input={"keywords": ["heat pump"], "location": "2840"})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

### Known limits

- **Current month:** Google Ads does not report the current month. The trend ends with the last complete month.
- **Rounding:** Google Ads rounds search volumes into ranges. Bing rounds volumes to the nearest ten.
- **Low-volume keywords:** keywords with very few searches may return no data (`NO_DATA`).
- **Adult content:** Google Ads may return no data for adult keywords.
- **Related keywords:** generated from the first 20 keywords of the list, in one request, up to `maxSuggestions`.
- **Bing:** English, French and German only; a location is required.
- **Speed:** the source accepts a limited number of requests per minute, so very large lists (20,000 keywords) take a few minutes.

### FAQ

**Is this the same data as the Google Ads Keyword Planner?**
Yes. The metrics come from Google Ads through the DataForSEO API, the same source used by many SEO tools. Values can differ slightly from what you see in your own Keyword Planner account, because Google shows broader ranges to accounts without active campaigns.

**Do I need a Google Ads account?**
No.

**Why is a keyword missing from the dataset?**
Google had no search volume for it in the selected location and language, or the keyword was invalid. Check its status in `RUN_SUMMARY`. You were not charged for it.

**How do I find a location code?**
You can type the location name (for example `Colombia` or `Texas,United States`). If a name is rejected, use the location code from the DataForSEO location list, for example 2840 (United States), 2170 (Colombia), 2724 (Spain), 2484 (Mexico) or 2826 (United Kingdom).

**Can I get data for a city?**
Yes, for the cities Google Ads supports. Use the full name, for example `Miami,Florida,United States`.

**What does "competition" mean?**
It measures how many advertisers bid on the keyword in paid search, not how hard it is to rank organically.

**How is this cheaper than tools that charge per keyword?**
The source charges per batch of up to 1,000 keywords, so this Actor charges a small batch fee and a fraction of a cent per keyword. A full batch costs USD 0.45, compared with about USD 10 at USD 0.01 per keyword.

**What happens if the run is restarted by the platform?**
Progress is saved after every batch. The run continues where it stopped without requesting, saving or charging anything twice.

**Can I use the data commercially?**
Yes, for your own analysis and for your clients. You are responsible for complying with the terms of the services you use the data with.

### Related Actors from ocius-data

Built to the same standard: a status for every input in `RUN_SUMMARY`, actionable error messages and list-based inputs, so they chain well in one workflow:

- **[Google Autocomplete API: Keyword Ideas & Questions](https://apify.com/ocius-data/google-autocomplete-keyword-ideas)**: hundreds of long-tail keyword ideas and questions from Google, YouTube and Shopping autocomplete.
- **[Google Trends API: Interest, Related & Trending Now](https://apify.com/ocius-data/google-trends-reliable)**: relative interest over time and by region, related queries and Trending Now.
- **[Bulk Domain Authority & Backlinks Checker API](https://apify.com/ocius-data/domain-authority)**: domain authority (0 to 100), backlinks and referring domains for up to 50,000 domains.
- **[Google Ads Transparency Center API - Competitor Ads](https://apify.com/ocius-data/google-ads-transparency-reliable)**: the Google Ads your competitors are running, with full creatives, and alerts when ads start or stop.

### Changelog

- **0.1 (November 2026)** — First release: Google Ads and Bing Ads search volume, CPC, competition, top-of-page bids, monthly trend with 3-month change (year-over-year when 13+ months are requested), related keywords, per-keyword statuses, run summary with billing reconciliation, cost-limit-aware batching and migration-safe progress.

# Actor input Schema

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

Keywords to measure, one per line. Up to 20,000 per run, sent in batches of 1,000. Duplicates are removed (case-insensitive). Google Ads accepts up to 80 characters and 10 words per keyword; Bing up to 100 characters.

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

Country, region or city by full name (for example "United States", "Colombia", "London,England,United Kingdom") or a DataForSEO location code (for example 2840 for the United States). Leave empty for worldwide data (Google only).

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

Language name (for example "English", "Spanish") or code (for example en, es). Bing supports English, French and German only.

## `searchEngine` (type: `string`):

Source of the metrics. Google Ads is the standard. Bing Ads adds Microsoft search volume and CPC.

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

Adds the search volume of each month in the date range (12 months by default).

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

First month of the trend, format YYYY-MM-DD. Up to 4 years back for Google and 2 years for Bing. Leave empty for the past 12 months.

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

Last month of the trend, format YYYY-MM-DD. Must be in a past month: Google Ads does not report the current month.

## `includeSuggestions` (type: `boolean`):

Finds related keyword ideas with full metrics from Google Ads, using the first 20 keywords as seeds. Each related keyword is billed as a "suggestion" event.

## `maxSuggestions` (type: `integer`):

Upper limit of related keywords returned, sorted by search volume (highest first).

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

Includes search partner sites in the metrics. Off by default, which matches Google search only.

## Actor input object example

```json
{
  "keywords": [
    "air conditioner",
    "heat pump",
    "mini split"
  ],
  "location": "United States",
  "language": "English",
  "searchEngine": "google",
  "includeMonthlySearches": true,
  "includeSuggestions": false,
  "maxSuggestions": 200,
  "searchPartners": false
}
```

# Actor output Schema

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

One row per keyword with data (and per related keyword, if requested).

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

Status of every keyword (OK, NO_DATA, INVALID_INPUT, ERROR, NOT_PROCESSED\_\*) and billing reconciliation.

# 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": [
        "air conditioner",
        "heat pump",
        "mini split"
    ],
    "location": "United States",
    "language": "English"
};

// Run the Actor and wait for it to finish
const run = await client.actor("ocius-data/keyword-metrics").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": [
        "air conditioner",
        "heat pump",
        "mini split",
    ],
    "location": "United States",
    "language": "English",
}

# Run the Actor and wait for it to finish
run = client.actor("ocius-data/keyword-metrics").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": [
    "air conditioner",
    "heat pump",
    "mini split"
  ],
  "location": "United States",
  "language": "English"
}' |
apify call ocius-data/keyword-metrics --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ocius-data/keyword-metrics"
        }
    }
}
```

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/wuIqwJ87MlXqnRCgf/builds/kOlxXRsCxziRv2nyX/openapi.json
