# Keyword Search Volume, CPC & Difficulty (`bjgomes/keyword-search-volume-cpc-difficulty`) Actor

Google search volume, CPC, competition, keyword difficulty, search intent and a monthly trend for up to 20,000 keywords per run, or related keyword ideas from seed keywords. Data from DataForSEO. No API key or Google Ads account needed. Pay per keyword with data.

- **URL**: https://apify.com/bjgomes/keyword-search-volume-cpc-difficulty.md
- **Developed by:** [Brian Gomes](https://apify.com/bjgomes) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 keyword 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

Keyword Search Volume, CPC & Difficulty gives you Google search volume, cost per click, competition, keyword difficulty, search intent and the trend for any list of keywords. Or give it a few seed keywords and get related keyword ideas with the same numbers.

### What it does

Give it keywords and a location. You get one row per keyword:

- **How many people search for it:** the average monthly Google searches, plus month-by-month numbers.
- **What advertisers pay:** cost per click and the top-of-page bid range from Google Ads.
- **How hard it is to rank:** keyword difficulty from 0 to 100.
- **What the searcher wants:** informational, navigational, commercial or transactional.
- **Which way it's going:** the change against last month, last quarter and last year.

In **Ideas** mode, give it up to 200 seed keywords and get up to 20,000 related keywords, each with the same numbers. Sort them by relevance or by search volume, and drop the ones below a minimum volume.

You don't need a Google Ads account or an API key. The data comes from [DataForSEO](https://dataforseo.com/), whose keyword numbers are built from Google Ads data.

Here's what five rows look like:

| keyword | searchVolume | cpc | competition | keywordDifficulty | searchIntent | trendYearlyPct |
|---|---|---|---|---|---|---|
| espresso machine | 135,000 | 1.12 | HIGH | 54 | commercial | 8 |
| best espresso machine | 49,500 | 1.35 | HIGH | 47 | commercial | 22 |
| how to descale espresso machine | 6,600 | 0.21 | LOW | 18 | informational | 4 |
| espresso machine with grinder | 22,200 | 1.09 | HIGH | 39 | transactional | -6 |
| breville barista express | 60,500 | 0.87 | HIGH | 35 | navigational | -12 |

(Example data.)

### Who it's for

- **SEO research.** Check a content plan's keywords before writing: volume, difficulty and intent in one table.
- **PPC planning.** See cost per click and bid ranges before you spend on ads.
- **Agencies.** Size a client's market from a seed list in one run, and hand them the spreadsheet.
- **AI agents and automations.** A small, fast call with a fixed output shape. A 1-keyword run takes a few seconds. It works through the Apify MCP server.

### Input

| Field | What it does | Default |
|---|---|---|
| Keywords | The keywords to look up (Metrics), or the seed keywords (Ideas). | none |
| What to get | `metrics` for your keywords, `ideas` for related keywords. | metrics |
| Location | A place name as DataForSEO writes it, like `United States` or `London,England,United Kingdom`, or a location code like `2840`. [Full list](https://docs.dataforseo.com/v3/dataforseo_labs/locations_and_languages/). | United States |
| Language | A language code like `en`, `de` or `es`, or a name like `English`. | en |
| Months of search history | How many months of monthly volumes per keyword. 0 leaves them out. | 12 |
| Maximum ideas | Ideas mode: stop after this many ideas. | 100 |
| Minimum monthly searches | Ideas mode: leave out ideas below this volume. | 0 |
| Sort ideas by | Ideas mode: `relevance` or `searchVolume`. | relevance |

Input field names in the API: `keywords`, `mode`, `location`, `language`, `historyMonths`, `maxIdeas`, `minSearchVolume`, `ideasSortBy`.

Up to 20,000 keywords per run in Metrics mode, and up to 200 seed keywords in Ideas mode. Keywords are lowercased and duplicates dropped. A keyword over 80 characters or 10 words, or one with an emoji, is skipped with an error row and isn't charged.

Example input:

```json
{
  "keywords": ["espresso machine", "coffee grinder", "pour over kettle"],
  "location": "United States",
  "language": "en"
}
```

Ideas mode:

```json
{
  "mode": "ideas",
  "keywords": ["espresso machine"],
  "maxIdeas": 500,
  "minSearchVolume": 100,
  "ideasSortBy": "searchVolume"
}
```

### Output

One row per keyword. Export it from the Console as CSV, JSON, Excel, XML or HTML, or read it through the Apify API.

| Field | What it holds |
|---|---|
| `type` | `result` for a keyword with a search volume, `no-data` when there's no volume for it in this location, `error` for a keyword that was skipped or a note about the run. |
| `keyword` | The keyword, lowercased. |
| `searchVolume` | Average monthly Google searches over the last 12 months. |
| `cpc` | Average cost per click in Google Ads, in USD. |
| `competition` | Google Ads competition for the paid results: `LOW`, `MEDIUM` or `HIGH`. |
| `competitionIndex` | The same as a number from 0 to 100. |
| `lowTopOfPageBid`, `highTopOfPageBid` | The bid range for an ad at the top of the page, in USD. |
| `keywordDifficulty` | How hard it is to reach Google's organic top 10, from 0 (easy) to 100 (hardest). |
| `searchIntent` | The main intent: `informational`, `navigational`, `commercial` or `transactional`. |
| `otherIntents` | Secondary intents, if any. |
| `trendMonthlyPct`, `trendQuarterlyPct`, `trendYearlyPct` | Search volume change in percent against last month, last quarter and last year. |
| `monthlySearches` | Searches per month, oldest first: `{"month": "2026-09", "searchVolume": 135000}`. |
| `seedKeywords` | Ideas mode: the seed keywords the idea came from. |
| `location`, `language` | What you asked for. |
| `dataUpdatedAt` | When DataForSEO last updated the keyword's numbers. |
| `error` | Empty on a `result` row. Otherwise it says why there's no data or what went wrong. |

Example row (JSON):

```json
{
  "type": "result",
  "keyword": "espresso machine",
  "searchVolume": 135000,
  "cpc": 1.12,
  "competition": "HIGH",
  "competitionIndex": 100,
  "lowTopOfPageBid": 0.61,
  "highTopOfPageBid": 2.05,
  "keywordDifficulty": 54,
  "searchIntent": "commercial",
  "otherIntents": ["transactional"],
  "trendMonthlyPct": -18,
  "trendQuarterlyPct": 0,
  "trendYearlyPct": 8,
  "monthlySearches": [{ "month": "2026-08", "searchVolume": 165000 }, { "month": "2026-09", "searchVolume": 135000 }],
  "location": "United States",
  "language": "en",
  "dataUpdatedAt": "2026-10-06 03:08:24 +00:00",
  "error": null
}
```

### Pricing

You pay per keyword with data. There's no monthly fee and no API key to buy.

- **Each keyword with a search volume:** $0.003, so $3 per 1,000. Keyword ideas cost the same.
- **Each run:** a $0.002 start charge.
- **Not charged:** keywords with no data (`no-data` rows), skipped keywords and error rows.

A worked example. 500 keywords, of which 460 have data: 460 × $0.003 + $0.002 = $1.38. An ideas run that returns 1,000 ideas: $3.00.

Set a maximum cost per run for a hard ceiling. The run only asks for as many keywords as fit under it, then stops and says how many it didn't look up.

On Apify's free plan, a run looks up at most 25 keywords (or returns 25 ideas) as a sample. Any paid Apify plan removes that limit.

### How it handles the hard cases

- **Keywords with no data.** Some keywords have too few searches for Google to report, or fall in categories Google Ads restricts. They come back as `no-data` rows and aren't charged.
- **Keywords Google Ads refuses.** Emojis, other 4-byte characters and some symbols (such as ☕ © ^) are skipped before any request, each with an error row that says why. If DataForSEO still refuses a batch because of one keyword, the batch is split until that keyword is alone. It gets its own error row and the rest are answered.
- **A location or language DataForSEO doesn't know.** The run stops at the first request and finishes with one error row. The row says how to write the location and links to the full list. Nothing is charged.
- **Big lists.** Keywords go 700 per request, the most DataForSEO takes. 20,000 keywords is 29 requests, a minute or two in all.
- **DataForSEO's own hiccups.** A rate limit, a timeout or an unavailable upstream is tried 3 times in all, with a pause between. If it still fails, you keep every row so far plus an error row that says how many keywords weren't looked up.
- **The run's time limit.** The run stops starting new requests a little before its timeout and says how many keywords are left. It isn't marked failed.
- **When a run is marked failed.** Only when this Actor's own setup is broken (its DataForSEO account), or when DataForSEO failed before anything was done.

### Compared with the free tools

Google Keyword Planner is free with a Google Ads account, but it shows volume ranges unless you're spending on ads, and it has no difficulty or intent. Google Trends shows direction, not volume. Free SEO extensions show numbers one page at a time.

This Actor fits when you have a list: hundreds or thousands of keywords in one table, the same check every month, or a call from your own code or an AI agent. It costs less per keyword than most keyword tool subscriptions if you don't need the rest of the suite.

Data from DataForSEO. Not made by or affiliated with Google or DataForSEO.

### FAQ

**Where do the numbers come from?**
From DataForSEO's keyword database, which is built from Google Ads data and updated monthly. Difficulty and intent are DataForSEO's own measures.

**Why does a keyword have no data?**
It's not in DataForSEO's database for that location and language, which usually means too few searches. Or Google Ads doesn't report it (some health, finance or adult topics). Those rows aren't charged.

**Why is the volume a round number?**
Google Ads reports volumes in buckets (1,000, 1,300, 1,600 and so on), and the tools built on it, this one included, pass that through.

**Can I get several countries?**
One location per run. Run it once per country, or save each as a task.

**Does it work with AI agents?**
Yes. It takes a plain list of keywords, returns the same fields every time, needs no API key and runs in seconds for a short list. Agents can find and run it through the Apify MCP server.

**Can I run it on a schedule?**
Yes. Use Apify Schedules to run the same list monthly and compare volumes over time.

### Limits

- One location and one language per run.
- Up to 20,000 keywords per run in Metrics mode, 200 seed keywords and 20,000 ideas in Ideas mode.
- Google search only. No Bing, YouTube or Amazon volumes in this version.
- Volumes are monthly averages from Google Ads data. They don't count every search and don't update daily.
- On Apify's free plan, at most 25 keywords or ideas per run.

### Integrations

This Actor runs on Apify, so everything the platform does works with it.

- **API.** Start a run and read the results from your own code, in any language. [Apify API](https://docs.apify.com/integrations/api)
- **Schedules.** Run your keyword list monthly and watch the trend. [Schedules](https://docs.apify.com/actors/running/schedules)
- **Tasks.** Save your input once as a task and run it again with one click, or on a schedule. [Tasks](https://docs.apify.com/actors/running/tasks)
- **Webhooks.** Get a call to your own URL when a run succeeds or fails. [Webhooks](https://docs.apify.com/integrations/webhooks)
- **Google Sheets and Drive.** Download the results as CSV or Excel and open them in Sheets. Or add a Google Sheets step after this Actor in Make or Zapier. Or send the files to a Google Drive folder. [Datasets and exports](https://docs.apify.com/storage/dataset), [Google Drive](https://docs.apify.com/integrations/drive)
- **Zapier, Make and n8n.** Start a run from another app and send the results on to the next step. [Zapier](https://docs.apify.com/integrations/zapier), [Make](https://docs.apify.com/integrations/make), [n8n](https://docs.apify.com/integrations/n8n)
- **AI agents (MCP).** Claude, ChatGPT and other MCP clients can find and run this Actor through the Apify MCP server. [Apify MCP server](https://docs.apify.com/integrations/mcp)
- **Other Actors.** Pass this Actor's results to another Actor when a run finishes. [Actor-to-Actor](https://docs.apify.com/integrations/actors)

### Works with

- [Site Audit](https://apify.com/bjgomes/site-audit-seo): an SEO score from 0 to 100 for every page, with a fix for each issue.
- [Bulk PageSpeed Insights for a Sitemap](https://apify.com/bjgomes/bulk-pagespeed-insights-for-a-sitemap): Core Web Vitals for every page in a sitemap.
- [Sitemap URL Extractor & Status Checker](https://apify.com/bjgomes/sitemap-url-extractor-status-checker): the full URL list of a site, with lastmod and status codes.
- [Broken Link Checker from Sitemap](https://apify.com/bjgomes/broken-link-checker-from-sitemap): every broken link on every page in a sitemap, with the page it's on.

### Support

Found a bug or need a feature? Open an issue on this Actor's **Issues** tab. Include the run ID and a keyword that shows the problem.

# Actor input Schema

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

The keywords to look up, one per item. In 'Metrics' mode each keyword gets one row (up to 20,000 per run). In 'Ideas' mode these are the seed keywords (up to 200) and you get related keywords back. Keywords are lowercased and duplicates dropped; a keyword over 80 characters or 10 words, or with an emoji, is skipped with an error row and not charged.

## `mode` (type: `string`):

'Metrics': search volume, CPC, competition, difficulty and intent for exactly the keywords you give. 'Ideas': related keywords for your seed keywords, each with the same metrics.

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

Where the searches happen: a country or place name as DataForSEO writes it ('United States', 'United Kingdom', 'London,England,United Kingdom'), or a DataForSEO location code (2840 is the United States). List: https://docs.dataforseo.com/v3/dataforseo_labs/locations_and_languages/

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

The searchers' language: a code such as 'en', 'de' or 'es', or a name such as 'English'.

## `historyMonths` (type: `integer`):

How many months of monthly search volumes to include per keyword, newest last. 0 leaves the monthly list out (smaller rows). The trend fields (month, quarter and year change) are always there.

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

Ideas mode only: stop after this many keyword ideas. Each idea is one charged row.

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

Ideas mode only: leave out ideas with fewer average monthly searches than this. 0 keeps every idea that has a volume.

## `ideasSortBy` (type: `string`):

Ideas mode only: closest to your seeds first, or most searched first.

## Actor input object example

```json
{
  "keywords": [
    "espresso machine",
    "coffee grinder"
  ],
  "mode": "metrics",
  "location": "United States",
  "language": "en",
  "historyMonths": 12,
  "maxIdeas": 100,
  "minSearchVolume": 0,
  "ideasSortBy": "relevance"
}
```

# Actor output Schema

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

One row per keyword (or keyword idea) with search volume, CPC, competition, keyword difficulty, search intent and trend, plus a row for any keyword that has no data or was skipped.

# 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": [
        "espresso machine",
        "coffee grinder"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("bjgomes/keyword-search-volume-cpc-difficulty").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": [
        "espresso machine",
        "coffee grinder",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("bjgomes/keyword-search-volume-cpc-difficulty").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": [
    "espresso machine",
    "coffee grinder"
  ]
}' |
apify call bjgomes/keyword-search-volume-cpc-difficulty --silent --output-dataset

```

## MCP server setup

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

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/rHVANUH6qOs6LahAn/builds/FuQUl90TAzs7BzKlC/openapi.json
