# Keyword Search Volume: Bulk Google CPC, Difficulty & Trends (`dtkfoundry/keyword-search-volume`) Actor

Google keyword search volume in bulk from real Keyword Planner data, no Google Ads account needed: monthly volume, up to 8 years of monthly history, trend, CPC, competition, keyword difficulty and intent for 36 countries. Plus keyword suggestions and ideas from seeds. $0.004 per keyword.

- **URL**: https://apify.com/dtkfoundry/keyword-search-volume.md
- **Developed by:** [DTK Foundry](https://apify.com/dtkfoundry) (community)
- **Categories:** SEO tools, Marketing, Developer tools
- **Security:** Limited permissions
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.70 / 1,000 keyword with search volumes

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: Bulk Google CPC, Difficulty & Trends

Google search volume for thousands of keywords at once, from real Google Keyword Planner data, with no Google Ads
account needed. For each keyword you get:

- average monthly searches, with up to about 8 years of month-by-month history
- the trend over the last month, quarter and year
- CPC and top-of-page bid range
- competition
- keyword difficulty (0-100)
- search intent

Also turns seed keywords into suggestions (phrases that contain the seed) or ideas (related keywords on the same
topic), highest volume first. 36 countries.

- **The same numbers as Keyword Planner.** Tested on 7,667 keywords across 15 countries against live Google Ads
  lookups: 96.8% were exactly equal, and 99% were within 25% (2026-10-27).
- **Reliable and fast.** In a 200-lookup test (18,420 keywords), 200 of 200 lookups succeeded. 700 keywords come
  back in about 6 seconds.
- **Bulk.** Up to 10,000 keywords per run, sent 700 at a time.
- **More than volume.** Difficulty, intent and trend come in the same row, so there is no second tool to run.
- **Fill the gaps (optional).** Long-tail keywords missing from the keyword database can be looked up live in
  Keyword Planner. In tests this found about half of the missing ones.
- **$0.004 per keyword with data**, plus $0.02 per run. Keywords without search volume are free.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `metrics` | `metrics` = numbers for your keyword list. `suggestions` = keywords that contain each seed. `ideas` = related keywords on the seed's topic. |
| `keywords` | array | `[]` | Metrics: up to 10,000 keywords. Suggestions and ideas: up to 20 seeds. Empty = a free format example. |
| `country` | string | `us` | us, gb, ca, au, nz, ie, in, sg, za, de, at, ch, fr, be, nl, es, mx, ar, co, cl, it, pt, br, pl, se, dk, no, fi, jp, kr, tr, id, ph, my, ae, sa. |
| `language` | string | country's main | Second options: us `es`, ca `fr`, in `hi`, sg `zh-CN`, ch `fr`/`it`, be `fr`/`de`, id `en`, ph `tl`, my `ms`, ae `ar`, sa `en`. |
| `maxResultsPerSeed` | integer | `100` | Suggestions and ideas: how many keywords per seed, highest volume first (up to 1,000). |
| `groupVariants` | boolean | `true` | Suggestions and ideas: Google gives reordered phrases the same numbers ("shoes running" = "running shoes"). On, each group is one row and the others are listed in `variants`. The actor reads 3 times `maxResultsPerSeed` phrases to fill the rows; for very broad seeds ("running shoes" gave 45 groups from 300 phrases) you get fewer rows, never more. |
| `minVolume` | integer | `0` | Leave out keywords with fewer monthly searches. They are not charged. |
| `historyMonths` | integer | `12` | Months of history per keyword, newest first (0-120). Metrics mode has up to about 8 years; suggestions and ideas have 12 months. |
| `fillGaps` | boolean | `false` | Metrics mode, paid Apify plans: look up keywords the database lacks live in Keyword Planner. |

```json
{
    "mode": "metrics",
    "keywords": ["standing desk", "running shoes", "crm software"],
    "country": "us",
    "historyMonths": 12
}
```

```json
{
    "mode": "suggestions",
    "keywords": ["standing desk"],
    "maxResultsPerSeed": 200,
    "minVolume": 100,
    "country": "gb"
}
```

### Output (one record per keyword)

```json
{
    "status": "ok",
    "keyword": "standing desk",
    "searchVolume": 135000,
    "cpc": 5.16,
    "competition": 1,
    "competitionLevel": "HIGH",
    "lowTopOfPageBid": 0.89,
    "highTopOfPageBid": 6.92,
    "keywordDifficulty": 38,
    "intent": "transactional",
    "trendMonthlyPct": -18,
    "trendQuarterlyPct": 0,
    "trendYearlyPct": 0,
    "monthlySearches": [{"month": "2026-09", "searches": 135000}, {"month": "2026-08", "searches": 165000}],
    "country": "us",
    "language": "en",
    "source": "google-ads-database",
    "dataUpdatedAt": "2026-10-15 22:33:34 +00:00"
}
```

- **Volumes:** `searchVolume` is Google's average monthly searches over the last 12 months. `monthlySearches` lists
  each month, newest first.
- **Trend:** `trendMonthlyPct`, `trendQuarterlyPct` and `trendYearlyPct` compare the latest month with 1, 3 and 12
  months earlier.
- **CPC and bids:** `cpc` is Google's average cost per click in USD. `lowTopOfPageBid` and `highTopOfPageBid` are
  the bid range advertisers paid to appear at the top of the page.
- **Competition:** `competition` (0-1) and `competitionLevel` (LOW / MEDIUM / HIGH) measure how many advertisers
  bid on the keyword. They are about paid search, not organic.
- **Difficulty and intent:** `keywordDifficulty` (0-100) estimates how hard it is to rank in the top 10 organic
  results. `intent` is informational, navigational, commercial or transactional.
- **Seed and variants:** in suggestions and ideas mode each record also has `seed`, and `variants`: the close
  variants Google counts as the same search (same volume and history), grouped into this row. One row is one
  charged keyword.
- **Source:**
  - `google-ads-database` comes from a keyword database built from Google Ads data and refreshed monthly.
  - `google-ads-live` rows come from a live Keyword Planner lookup (`fillGaps`). These have the year-on-year trend
    only, and no difficulty or intent.
- **Free records:**
  - `status: "no-data"`: Google has no volume for the keyword in that country.
  - `status: "invalid"`: a keyword Google does not accept (over 80 characters, more than 10 words, emoji or
    symbols such as `!` or `(`).
  - `status: "failed"`: a lookup that failed after retries.
  - `status: "no-results"`: a seed with no suggestions or ideas.

### Pricing

Prices below are for Apify's Free plan. Paid Apify plans pay less per run and per keyword: Bronze 10% off, Silver 20% off, Gold and above about 33% off. The Google Ads lookup costs the same on every plan.

Pay per event, no subscription.

| Event | Price | When it is charged |
|---|---|---|
| Run | **$0.02** | Once per run, when the first keyword data arrives. Runs that fail before that are free. |
| Keyword | **$0.004** | Per keyword delivered with a search volume ($4 per 1,000). Keywords without data are free, as are invalid ones and those under `minVolume`. |
| Google Ads lookup | **$0.15** | Only with `fillGaps`: one live Keyword Planner lookup of up to 1,000 missing keywords. |

What a run costs:

| Run | Cost |
|---|---|
| 100 keywords | about $0.42 |
| 1,000 keywords | about $4.02 |
| 10,000 keywords | about $40 |
| 1 seed, 100 suggestions or ideas | about $0.42 |
| 1,000 long-tail keywords with `fillGaps`: 850 in the database, 75 of the other 150 filled | about $3.87 |

Set a maximum charge in the run options and the actor stops before it would go over: it looks up only as many
keywords as fit. Apify's standard run-start event adds $0.00005 per run.

### Limits, stated plainly

- **Google's numbers, Google's rounding.** Volumes come in Google's buckets (for example 135,000, then 165,000).
  Close variants share one volume ("running shoe" and "running shoes").
- **Monthly data.** The database refreshes once a month, so the newest month is last month.
- **Rare keywords have no volume.** In tests, 97.6% of common keywords (head terms, questions, brands, local) had
  one. For long-tail autocomplete phrases it was 84% in the database and 92% with `fillGaps`. Google withholds data
  for some topics entirely (for example "vpn" in the US).
- **CPC is in USD.**
- **Difficulty and intent are estimates.** They are not numbers from Google.
- **`fillGaps` needs a paid Apify plan.** It is ignored on the Free plan, and the status message says so. Live
  lookups are spaced a few seconds apart, so a large fill takes longer.
- **Run limits:** up to 10,000 keywords per run, or 20 seeds with up to 1,000 results each.

### Privacy

Only the keywords you give are sent for lookup. Results are stored only in your run's dataset. The actor runs with
limited permissions and reads nothing else in your account.

# Actor input Schema

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

Metrics: search volume and metrics for your keyword list. Suggestions: keywords that contain each seed ("standing desk" -> "standing desk with drawers"). Ideas: related keywords on the same topic as each seed ("standing desk" -> "ergonomic chair").

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

Metrics mode: up to 10,000 keywords, one per line. Suggestions and ideas: up to 20 seed keywords. Leave empty for a free format example.

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

Where the searches are counted.

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

Language code of the searches. Empty = the country's main language. Countries with a second option: us es, ca fr, in hi, sg zh-CN, ch fr/it, be fr/de, id en, ph tl, my ms, ae ar, sa en.

## `maxResultsPerSeed` (type: `integer`):

How many keywords to return for each seed, highest search volume first. Each one is a charged keyword. Up to 1,000.

## `groupVariants` (type: `boolean`):

Google gives reordered phrases the same numbers ("shoes running", "running and shoes" = "running shoes"). On: one row per group, the others listed in variants. The actor reads 3x the rows you ask for to fill them; very broad seeds can return fewer rows. Off: every phrase is its own row.

## `minVolume` (type: `integer`):

Leave out keywords with fewer monthly searches (not charged). 0 = keep all.

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

How many months of monthly search counts to include per keyword, newest first. Metrics mode has up to about 8 years; suggestions and ideas have 12 months. 0 = none.

## `fillGaps` (type: `boolean`):

Metrics mode, paid Apify plans: keywords missing from the keyword database are looked up live in Google Keyword Planner, up to 1,000 per lookup ($0.15 per lookup, plus $0.004 per keyword found). Useful for long-tail lists: in tests it found about half of the missing ones.

## Actor input object example

```json
{
  "mode": "metrics",
  "keywords": [
    "standing desk",
    "running shoes",
    "crm software"
  ],
  "country": "us",
  "maxResultsPerSeed": 100,
  "groupVariants": true,
  "minVolume": 0,
  "historyMonths": 12,
  "fillGaps": false
}
```

# Actor output Schema

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

Every keyword with its metrics.

## `resultsCsv` (type: `string`):

The same keywords as CSV (monthly history flattened).

# 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": [
        "standing desk",
        "running shoes",
        "crm software"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dtkfoundry/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 = { "keywords": [
        "standing desk",
        "running shoes",
        "crm software",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("dtkfoundry/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 '{
  "keywords": [
    "standing desk",
    "running shoes",
    "crm software"
  ]
}' |
apify call dtkfoundry/keyword-search-volume --silent --output-dataset

```

## MCP server setup

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