# Keyword Search Volume, CPC & Competition — Keyword Difficulty (`afanasenko/keyword-search-volume`) Actor

Google keyword search volume, CPC, competition and keyword difficulty from keyword planner data for SEO research. Check lists of keywords or generate keyword ideas with monthly search history. Export to CSV, JSON or Excel.

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

## Pricing

from $8.00 / 1,000 keywords

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

## Keyword Search Volume, CPC & Competition — Keyword Difficulty

[![Apify Actor](https://apify.com/actor-badge?actor=afanasenko/keyword-search-volume)](https://apify.com/afanasenko/keyword-search-volume)

> **Check Google keyword search volume or find keyword ideas, with CPC, advertising competition, keyword difficulty and monthly search history.** Export a keyword planner table for SEO research, content planning or advertising research.

> **🆓 Start on the free plan:** check one keyword with full metrics, or get ten ideas in full. More rows are shown locked, within a monthly allowance. Full rows and completed lookups use your Apify credits.

### 🎯 What it does

**Choose the operation mode first.** Each mode has its own keyword input and result limit.

| Mode | You give | You receive |
|---|---|---|
| **Mode 1: Check Keyword Metrics** | A list of up to 1,000 keyword phrases | One row per matched keyword, in your input order |
| **Mode 2: Find Keyword Ideas** (default) | Exactly one seed phrase | Related keyword ideas; 100 by default, up to 1,000 |

Both modes include search volume, CPC in USD, advertising competition, organic keyword difficulty and monthly history when available. Mode 2 also includes search intent.

Some keywords have no reported volume. Their other metrics remain available, with `status: volume_unavailable`. Values vary by country and language, and update dates vary by keyword.

### 📦 Output sample

Each row describes one keyword:

- **Market and keyword:** `keyword`, `country`, `language`, `mode`.
- **Volume and advertising:** `searchVolume`, `cpcUsd`, `competition`, `competitionLevel`, `lowTopOfPageBidUsd`, `highTopOfPageBidUsd`.
- **SEO and intent:** `keywordDifficulty`, `coreKeyword`, `categories`, `detectedLanguage`, `isAnotherLanguage`, `intent`, `secondaryIntents`.
- **History and availability:** `monthlySearches`, `volumeTrend`, `metricsUpdatedAt`, `status`, `isLocked`.

Missing metrics are `null`. A true zero remains `0`. Locked rows have null metrics; Mode 2 also hides the keyword itself.

A real Mode 2 row from the US/en validation run on 6 October 2026:

```json
{
  "keyword": "on running shoes",
  "country": "US",
  "language": "en",
  "mode": "ideas",
  "searchVolume": 201000,
  "cpcUsd": 1.42,
  "competition": 1,
  "competitionLevel": "HIGH",
  "lowTopOfPageBidUsd": 0.42,
  "highTopOfPageBidUsd": 2.43,
  "keywordDifficulty": 27,
  "monthlySearches": [
    {
      "year": 2026,
      "month": 8,
      "searchVolume": 49500
    },
    {
      "year": 2026,
      "month": 7,
      "searchVolume": 74000
    },
    {
      "year": 2026,
      "month": 6,
      "searchVolume": 450000
    },
    {
      "year": 2026,
      "month": 5,
      "searchVolume": 301000
    },
    {
      "year": 2026,
      "month": 4,
      "searchVolume": 246000
    },
    {
      "year": 2026,
      "month": 3,
      "searchVolume": 135000
    },
    {
      "year": 2026,
      "month": 2,
      "searchVolume": 74000
    },
    {
      "year": 2026,
      "month": 1,
      "searchVolume": 74000
    },
    {
      "year": 2025,
      "month": 12,
      "searchVolume": 550000
    },
    {
      "year": 2025,
      "month": 11,
      "searchVolume": 90500
    },
    {
      "year": 2025,
      "month": 10,
      "searchVolume": 165000
    },
    {
      "year": 2025,
      "month": 9,
      "searchVolume": 110000
    }
  ],
  "volumeTrend": {
    "monthly": -33,
    "quarterly": -89,
    "yearly": -55
  },
  "categories": [
    10014,
    13605,
    13616
  ],
  "coreKeyword": "running shoes",
  "detectedLanguage": "en",
  "isAnotherLanguage": false,
  "intent": "transactional",
  "secondaryIntents": [
    "informational"
  ],
  "metricsUpdatedAt": "2026-09-14T16:55:29.000Z",
  "status": "ok",
  "isLocked": false
}
```

**Views** in Storage → Dataset show the same rows with different columns:

| View | Row | Best for |
|---|---|---|
| **Keyword metrics** | One keyword, trimmed to volume, CPC, competition and difficulty | Comparing a keyword list |
| **Keyword ideas** | The same rows, trimmed to volume, CPC, difficulty and intent | Finding content opportunities |
| **Monthly search history** | The same rows, trimmed to history, trends and update dates | Checking seasonality |

The full field descriptions are in [the dataset schema](.actor/dataset_schema.json).

### ⚡ Quick start

Select **Mode 2: Find Keyword Ideas**, then enter one seed:

```json
{
  "operationMode": "ideas",
  "seedKeywords": ["running shoes"],
  "maxIdeas": 100,
  "country": "US",
  "language": "en"
}
```

Run it and open Storage → Dataset. On the free plan, ten ideas are shown in full and up to forty more are locked. On a paid plan, up to your requested limit are shown in full.

### 🧭 When to use it

- Compare search volume and difficulty before writing content.
- Build a keyword planner table with CPC and advertising competition.
- Find related ideas and their search intent from a seed phrase.
- Check monthly history for seasonal topics.
- Research another country in its supported language.

### 🛠 Input

**Choose your mode first**, then fill the matching section:

| Field | Meaning | Default |
|---|---|---|
| `operationMode` | `metrics` or `ideas` | `ideas` |
| `keywords` | Mode 1: one keyword phrase per line; commas also separate phrases | Empty |
| `maxKeywords` | Mode 1: how many input keywords to check, 1–1,000 | 1,000 |
| `seedKeywords` | Mode 2: exactly one seed phrase | Empty |
| `maxIdeas` | Mode 2: idea limit, 1–1,000 | 100 |
| `country` | Country for the metrics | `US` |
| `language` | Supported search language for that country | `en` |

Spaces stay inside a phrase. Duplicates are removed without changing the order. Each keyword can have at most 80 characters and ten words. An unsupported country/language pair or malformed input is rejected before anything is looked up or charged.

Set **Maximum cost per run** in the run options to cap charges. It limits work before each lookup. If the main keyword field of your chosen mode is empty, the run announces a built-in sample, capped at ten rows; it still follows the free-plan limits.

### 💰 How much will keyword research cost?

Apify Pay Per Event charges for full matched keyword rows and completed lookups:

| Event | Free | Bronze | Silver | Gold and up |
|---|---|---|---|---|
| **Keyword** | $0.012 | $0.01067 | $0.00933 | $0.008 |
| **Keyword lookup** | $0.030 | $0.025 | $0.022 | $0.020 |

A lookup is one checked batch of up to 700 input keywords or one ideas page. An empty successful result still costs one lookup; it costs no keyword events. Missing keywords and locked rows are not charged. Matched rows with unavailable volume are charged because they contain other metrics. Unfinished lookups are not charged.

| Example | Bronze charge |
|---|---|
| 3 matched input keywords | $0.05701 |
| 50 matched input keywords | $0.55850 |
| 100 keyword ideas | $1.09200 |
| 1,000 matched input keywords, two batches | $10.72000 |
| Empty result, one lookup | $0.02500 |

The platform may include its small standard start event. Your run ceiling applies to the complete run.

### 🆓 Free vs paid plan

| | Free plan | Paid plan |
|---|---|---|
| Mode 1 full keyword rows | 1 per run | Up to 1,000 |
| Mode 1 extra locked rows | Up to 49; your input keyword remains visible | All delivered rows in full |
| Mode 2 full ideas | Up to 10 per run | Up to 1,000 |
| Mode 2 extra locked rows | Up to 40; keyword and metrics hidden | All delivered rows in full |
| Monthly allowance | Typically five full batches; shorter or empty results may allow more | Free-plan allowance lifted |

Sample runs count toward the monthly allowance. If less allowance remains, the run requests fewer rows and states the actual full and locked counts. The allowance resets at the start of each UTC calendar month. Full rows and completed lookups consume credits even on the free plan.

[Upgrade your Apify plan](https://console.apify.com/billing/subscription) and run the same input again for full results. Paid plans start at $19/mo with $19 in platform credits.

### 🧪 Filter recipes

These recipes choose a mode and a market. No post-fetch filters remove paid results.

**Check your keyword list — Mode 1**

```json
{"operationMode":"metrics","keywords":["running shoes","pizza near me","seo agency"],"country":"US","language":"en","maxKeywords":1000}
```

**Find Spanish ideas — Mode 2**

```json
{"operationMode":"ideas","seedKeywords":["zapatillas de correr"],"country":"ES","language":"es","maxIdeas":100}
```

### 🔁 Resume / checkpoint

Already saved rows remain in the dataset when a run is stopped. For a new run, paste the unfinished input keywords again; Skipped Items lists them and their reasons. A new run charges for new completed work. It does not resume the prior dataset automatically.

### 📡 Live status & storage records

The Output tab links to the live status page and results. After completion, the stored status page shows the saved count and the run's message.

| Record | Contents |
|---|---|
| **Run Summary** | Full and locked rows, completed lookups, prices, skipped count and stop reason |
| **User Message** | What happened and what to change; always present when no full rows were saved |
| **Skipped Items** | Input keywords not delivered, with the reason |
| **Free-Plan Limits Applied** | Limits that affected a free-plan run |

### 🤖 Programmatic / API use

Choose `operationMode` explicitly in automated calls. Use the same inputs as the form with [Apify's API](https://docs.apify.com/api/v2/acts-run-sync-get-dataset-items):

```bash
curl -X POST 'https://api.apify.com/v2/acts/afanasenko~keyword-search-volume/run-sync-get-dataset-items' \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"operationMode":"ideas","seedKeywords":["running shoes"],"maxIdeas":100}'
```

AI agents can call the actor through Apify's MCP server. Download the complete dataset as JSON, CSV or Excel.

### 💡 Tips & Best Practices

#### Pick a market first

Volume for the same phrase can differ greatly by country. Use a language supported for that market.

#### Use a clear seed

Mode 2 accepts one phrase. For a product range, start separate runs for distinct topics.

#### Read difficulty and competition separately

Difficulty estimates organic ranking effort. Competition measures advertisers, not organic results.

#### Check the update date

Monthly averages and history are reported data, not a count of today's searches. Compare update dates before judging changes.

### ❓ FAQ

**How do I check Google keyword search volume?** Choose Mode 1 and enter keyword phrases, a country and a language. Export the matched rows.

**How do I find keyword ideas?** Choose Mode 2 and enter exactly one seed. It returns related ideas with volume, CPC, difficulty and intent when known.

**Is there a free keyword search volume option?** The free Apify plan shows one checked keyword or ten ideas in full within a monthly allowance. Full rows and lookups use credits.

**Can I use Google Keyword Planner instead?** Yes. This actor is useful when you want an export or an automated workflow with the same input and output shape on each run.

**Does it return Ahrefs keyword difficulty?** No. Difficulty is an independent 0–100 estimate of the effort to rank in the organic top ten.

**Is advertising competition the same as difficulty?** No. Advertising competition is a 0–1 value about advertisers. Organic difficulty has its own column.

**Why is search volume null?** Some matched keywords have no reported average volume. Other available metrics are kept and the full matched row is charged. A missing keyword gets no dataset row and no keyword charge.

**Do empty results cost anything?** A successfully checked empty batch or ideas page costs one lookup. No keyword rows are charged.

**How many months of history are returned?** It varies by keyword and mode. Ideas commonly have twelve months; list metrics can have longer history. Check the array and update date for each row.

**Can I export CSV or Excel?** Yes. Open the dataset and select Export. JSON keeps the history arrays in their original structure.

**Why are rows locked?** Your free-plan limits apply. Locked rows are not charged. Upgrade and run the same input again to see full results.

**What happens with empty input?** The run announces a sample and returns at most ten rows, subject to your plan and ceiling. Put your own phrase in the matching mode section for real research.

### 🔗 Other SEO actors (same author)

| Actor | Purpose |
|---|---|
| [Backlink Scraper](https://apify.com/afanasenko/backlink-scraper) | Check linking pages, anchor text, dofollow, dates and spam score |

### 🛟 Support & feedback

1. Open the Apify Store **Issues** tab with your run link if results differ from your expectations.
2. Include the selected mode, country and language so the query can be checked.
3. Leave a review if the actor helped your keyword research.

### ⚖️ Is it legal to research keyword search volume?

Use keyword metrics for lawful research and respect the terms of the services in your workflow. See [Apify's guidance on web scraping](https://blog.apify.com/is-web-scraping-legal/).

# Changelog

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

# Actor input Schema

## `operationMode` (type: `string`):

Choose the mode first. Mode 1 checks your keyword list; Mode 2 finds ideas from one seed.

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

One keyword phrase per line. Commas also separate keywords; spaces stay inside a phrase. Up to 80 characters and 10 words per keyword.

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

The most keywords checked from your list. Extra input keywords are listed in Skipped Items.

## `seedKeywords` (type: `array`):

Exactly one keyword phrase to find related ideas.

## `maxIdeas` (type: `integer`):

The most keyword ideas to return.

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

Country used for keyword search volume and advertising metrics.

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

Search language. The selected language must be supported for the selected country.

## Actor input object example

```json
{
  "operationMode": "ideas",
  "keywords": [],
  "maxKeywords": 1000,
  "seedKeywords": [],
  "maxIdeas": 100,
  "country": "US",
  "language": "en"
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `ideas` (type: `string`):

No description

## `history` (type: `string`):

No description

## `liveStatus` (type: `string`):

No description

## `userMessage` (type: `string`):

No description

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

No description

## `freeLimitsApplied` (type: `string`):

No description

## `skippedItems` (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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("afanasenko/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 = {}

# Run the Actor and wait for it to finish
run = client.actor("afanasenko/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 '{}' |
apify call afanasenko/keyword-search-volume --silent --output-dataset

```

## MCP server setup

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