# Keyword Search Volume, CPC & Competition (Google Ads data) (`fairfetch/keyword-search-volume`) Actor

Google Ads keyword data in bulk: monthly search volume, CPC, competition, top-of-page bids and 12-month trend for up to 20,000 keywords, any country and language, plus optional keyword ideas. $3 per 1,000 keywords + $0.10 per fresh lookup (up to 1,000 keywords each).

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

## Pricing

from $3.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.

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 (Google Ads data)

Get **Google Ads keyword data in bulk**: average **monthly search volume**, **CPC**, **competition** (0-1 and LOW / MEDIUM / HIGH), **top-of-page bids** and the **12-month search trend** for up to 20,000 keywords per run, in any country and language. Optionally add **related keyword ideas** with the same metrics. No Google Ads account, no developer token, no Keyword Planner limits.

**Price: $3 per 1,000 keywords** ($0.003 per keyword) **+ $0.10 per fresh lookup** (one lookup covers up to 1,000 keywords, or one keyword-ideas request). Keywords that Google Ads has no data for are free. A lookup in which no keyword has data has no lookup fee, and neither do rows you already paid for in the last 7 days (served from the cache). No extra usage costs.

### What you get

- One row per keyword: search volume, CPC (USD), competition index (0-1) and level, low and high top-of-page bid (USD), monthly searches for the last 12 months, location, language, and whether the row is your keyword (`seed`) or a keyword idea (`idea`).
- **Google Ads data** (the numbers behind Google Keyword Planner), fetched through the DataForSEO Keywords Data API.
- **Any location**: 100+ countries by two-letter code or name, plus states, regions and cities by DataForSEO / Google Ads location code (for example 2840 = United States) or full name (`London,England,United Kingdom`), or worldwide.
- **Keyword ideas**: related keywords from Google Ads for your first 20 keywords, filtered by your minimum volume.
- **Clean input handling**: duplicates removed, keywords lowercased, symbols Google Ads rejects (`! @ % * ( ) ?`, emojis and the rest of the data provider's published list) removed before sending, because one such character can make Google Ads reject a whole batch. Keywords over Google's limits (80 characters, 10 words) or with a letter Google Ads refuses are skipped. The run summary lists every changed and skipped keyword.
- **Filters and sorting**: minimum monthly volume, sort by volume, CPC, competition, A-Z or your input order.
- Built for automation and AI agents: predictable schema, pay-per-event pricing without usage fees, limited permissions, a free `SUMMARY` record, and a clean stop when your spending limit is reached.

### Use cases

- **SEO keyword research**: find which of your candidate keywords people actually search for, and how often.
- **PPC planning**: estimate CPC and top-of-page bids before you build a Google Ads campaign, and spot keywords where competition is low.
- **Content planning**: prioritise article and landing-page topics by demand; use the 12-month trend to time seasonal content.
- **Validating autocomplete ideas**: collect long-tail ideas with [Search Autocomplete](https://apify.com/fairfetch/search-autocomplete) (Google, YouTube, Bing, Amazon suggestions), then check here which ones have real search volume.
- **Market research**: compare demand for the same keywords across countries and languages.
- **AI agents and workflows**: give an agent a cheap, reliable tool to check search demand before it writes, plans or recommends.

### How to use

1. Paste your **Keywords**, one per line.
2. Choose a **Location** (default: United States) and, if needed, a **Language** (empty = the main language of the location).
3. Optional: turn on **Add keyword ideas**, set a **Minimum monthly search volume**, choose how to **Sort results**.
4. Run the Actor and download the results as JSON, CSV, Excel or via API. Counts, changed and skipped keywords, batches without data and any error are in the free `SUMMARY` record in the run's key-value store.

#### Input example

The same request as DataForSEO's documented example (three keywords, United States):

```json
{
  "keywords": ["buy laptop", "cheap laptops for sale", "purchase laptop"],
  "location": "us",
  "language": "en",
  "includeMonthlySearches": true,
  "sortBy": "searchVolume"
}
```

#### Output example

The figures below are taken from **DataForSEO's documented sample response** for that request (United States, data up to October 2023), so you can see the exact format. They are not current numbers; your run returns the latest monthly data from Google Ads.

```json
{
  "keyword": "cheap laptops for sale",
  "searchVolume": 6600,
  "cpc": 1.25,
  "competition": 1.0,
  "competitionLevel": "HIGH",
  "lowTopOfPageBid": 0.38,
  "highTopOfPageBid": 2.78,
  "monthlySearches": [
    { "year": 2022, "month": 11, "volume": 8100 },
    { "year": 2022, "month": 12, "volume": 8100 },
    { "year": 2023, "month": 1, "volume": 8100 },
    { "year": 2023, "month": 2, "volume": 6600 },
    { "year": 2023, "month": 3, "volume": 8100 },
    { "year": 2023, "month": 4, "volume": 6600 },
    { "year": 2023, "month": 5, "volume": 6600 },
    { "year": 2023, "month": 6, "volume": 6600 },
    { "year": 2023, "month": 7, "volume": 6600 },
    { "year": 2023, "month": 8, "volume": 8100 },
    { "year": 2023, "month": 9, "volume": 6600 },
    { "year": 2023, "month": 10, "volume": 6600 }
  ],
  "location": "United States",
  "locationCode": 2840,
  "language": "en",
  "source": "seed",
  "hasData": true,
  "spellCorrection": null,
  "scrapedAt": "2026-10-17T12:00:00Z"
}
```

All three keywords from that documented sample:

| keyword | searchVolume | cpc | competition | competitionLevel | lowTopOfPageBid | highTopOfPageBid |
|---|---|---|---|---|---|---|
| cheap laptops for sale | 6,600 | 1.25 | 1.0 | HIGH | 0.38 | 2.78 |
| buy laptop | 2,900 | 7.95 | 1.0 | HIGH | 1.69 | 10.04 |
| purchase laptop | 110 | 12.74 | 0.99 | HIGH | 2.07 | 15.43 |

#### Output fields

| Field | Description |
|---|---|
| `keyword` | Your keyword (lowercased, as sent to Google Ads) or the keyword idea |
| `searchVolume` | Average monthly searches over the last 12 months. Google Ads reports rounded values (for example 110, 2,900, 6,600) |
| `cpc` | Average cost per click in USD |
| `competition` | Paid-search competition from 0 to 1 (Google Ads competition index divided by 100) |
| `competitionLevel` | `LOW`, `MEDIUM` or `HIGH` |
| `lowTopOfPageBid`, `highTopOfPageBid` | Bid ranges for the top of the first page (about the 20th and 80th percentile of winning bids), USD |
| `monthlySearches` | `[{year, month, volume}]` for the last 12 months, oldest first; `null` if you turned the history off |
| `location`, `locationCode` | Location used and its Google Ads / DataForSEO code (`null` for worldwide) |
| `language` | Google Ads language code used |
| `source` | `seed` = one of your keywords, `idea` = a related keyword idea |
| `hasData` | `false` only for keywords without data that you chose to include |
| `spellCorrection` | The spelling Google Ads used if it corrected a misspelled keyword, otherwise `null` |
| `scrapedAt` | When the data was retrieved from Google Ads (UTC) |

The dataset has three views: **Keywords** (metrics table), **12-month trend** and **All fields**.

### Locations and languages

Common locations (type the code or the name):

| Code | Location | Code | Location | Code | Location |
|---|---|---|---|---|---|
| `us` | United States (2840) | `de` | Germany (2276) | `br` | Brazil (2076) |
| `gb` / `uk` | United Kingdom (2826) | `fr` | France (2250) | `mx` | Mexico (2484) |
| `ca` | Canada (2124) | `es` | Spain (2724) | `jp` | Japan (2392) |
| `au` | Australia (2036) | `it` | Italy (2380) | `kr` | South Korea (2410) |
| `in` | India (2356) | `nl` | Netherlands (2528) | `sg` | Singapore (2702) |
| `ie` | Ireland (2372) | `se` | Sweden (2752) | `ae` | United Arab Emirates (2784) |

100+ countries are built in. For states, regions and cities, use their DataForSEO location code or full name (for example `London,England,United Kingdom`); DataForSEO publishes the full list. Use `worldwide` for all locations. Locations in Russia and Belarus are not available from the data provider.

Languages: Google Ads language codes or names, such as `en`, `de`, `fr`, `es`, `pt`, `it`, `nl`, `ja`, `ko`, `hi`, `ar`, `tr`, `pl`, `zh-CN`, `zh-TW`, `he`, or `German`. Regional variants like `pt-BR` or `en-GB` use the base language. If you leave it empty, the main language of the location is used (English for worldwide).

### Keyword ideas

Turn on **Add keyword ideas** to get related keywords from Google Ads (the same kind of suggestions Keyword Planner shows) for your first 20 keywords. Ideas come in Google's relevance order, ideas without data or below your minimum volume are skipped, and ideas already in your list are not repeated. **Max keyword ideas** (default 100, up to 5,000) caps how many are added. Idea rows have `source: "idea"` and cost the same as any other row.

### Pricing

Pay per event, no subscription and no extra usage fees:

| Event | Price |
|---|---|
| `keyword`: one keyword row in the dataset (your keywords and ideas) | **$0.003** ($3 per 1,000) |
| `keyword-lookup`: one fresh request to Google Ads data (a batch of up to 1,000 keywords, or one keyword-ideas request). Charged when the request returns at least one keyword with data, even if your minimum volume then removes every row | **$0.10** |
| Actor start (standard Apify event, per run) | $0.00005 |

Examples:

- 5 keywords: **$0.115** ($0.10 lookup + $0.015)
- 100 keywords: **$0.40**
- **1,000 keywords = $3.10**
- 10,000 keywords: $31 (10 lookups)
- 20 keywords plus 100 keyword ideas: $0.56 (2 lookups)
- The same list again within 7 days, after a run that saved every keyword with data: keyword price only, no lookup fee

Good to know:

- **Keywords without data are free** (unless you turn on **Include keywords without data**). So are keywords below your minimum volume, skipped invalid keywords, duplicates and the `SUMMARY` record.
- **No lookup fee without data**: a lookup in which Google Ads has data for none of the keywords, or a request that fails, costs nothing. When Google Ads did return data, the fee applies even if your **Minimum monthly search volume** removes every row; the status message and the summary then say so.
- Your run's **Maximum cost per run** (or `maxTotalChargeUsd` via API) caps spending. Before every lookup the Actor checks that the rest of your limit covers the $0.10 fee **plus** at least 10 keyword rows (or every keyword of a smaller batch); otherwise it doesn't start the lookup and the status message tells you the minimum. One lookup for 10 or more keywords therefore needs a limit of at least **$0.14** ($0.10 fee + 10 rows + the tiny Apify start fee). If the limit is reached while a lookup's rows are being saved, the best rows of that lookup (per your sort order) are kept.
- If you check the same keywords again within 7 days (setting **Re-use results for**), the rows you already paid for are reused from the cache: same price per row, no lookup fee for them. Keywords you didn't get a row for (filtered out by your minimum volume, or beyond your spending limit) are looked up again, which is a new lookup. Set it to 0 to always fetch fresh data.

### Use it from the API, an app or an AI agent

Run synchronously and get the rows in one call (keep your Apify API token in the `APIFY_TOKEN` environment variable):

```bash
curl -X POST "https://api.apify.com/v2/acts/fairfetch~keyword-search-volume/run-sync-get-dataset-items?maxTotalChargeUsd=1" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords": ["standing desk", "standing desk converter"], "location": "gb", "includeKeywordIdeas": true, "maxIdeas": 50}'
```

The synchronous endpoint waits at most 300 seconds, and one lookup takes about 10-45 seconds. For more than about 3,000 keywords, or when you add keyword ideas to a long list, start the run asynchronously (`POST https://api.apify.com/v2/acts/fairfetch~keyword-search-volume/runs`) and read the dataset when the run has finished.

`keywords` also accepts objects such as `{"keyword": "..."}` or `{"suggestion": "..."}`, so you can pass dataset items from [Search Autocomplete](https://apify.com/fairfetch/search-autocomplete) directly. The Actor runs with limited permissions and pay-per-event pricing without usage fees, so AI agents can call it through the Apify MCP server and pay per run.

### Performance and limits

- Up to 20,000 keywords per run, sent to the data provider in batches of 1,000. Up to 1,000 keywords is a single lookup. Lookups run one after another and each takes **about 10-45 seconds** (measured on real runs), so a run of up to 1,000 keywords usually finishes within a minute and 20,000 keywords (20 lookups) can take up to about 15 minutes.
- Rows are saved after every lookup, so a timeout, an abort or a provider outage never loses rows you already paid for. The Actor doesn't start a lookup when less than a minute of the run's timeout is left.
- Temporary errors and rate limits are retried automatically with backoff. A request that may already have reached the provider (for example after a timeout while waiting for the answer) is not sent again automatically. If the provider stays unavailable, everything saved so far stays in the dataset and the run summary explains what happened.
- If Google Ads rejects one batch (for example because of a keyword it doesn't accept), the other batches still run; the summary lists the rejected batch under `failedLookups`.
- Google Ads limits: at most 80 characters and 10 words per keyword. Keywords that don't fit are listed in the summary and never charged.

### FAQ

**Where does the data come from?**
From Google Ads (the data behind Google Keyword Planner), retrieved through the DataForSEO Keywords Data API. The Actor does not scrape Google search pages.

**What does "search volume" mean exactly?**
The average number of monthly searches over the last 12 months for the chosen location and language, on Google Search (add search partners in the advanced settings if you want them). Google Ads rounds these values into buckets, so you will see numbers like 110, 2,900 or 6,600. The `monthlySearches` field gives the month-by-month figures.

**Why do some keywords have no data?**
Google Ads returns no data for very rare keywords, some restricted topics and some keywords it can't match. Those keywords are left out and are not charged (the summary counts them). Turn on **Include keywords without data** if you want them in the dataset with empty metrics.

**A whole batch came back without data. Why?**
According to the data provider, one keyword in a restricted category (for example weapons, drugs, tobacco or gambling) can make Google Ads return no data for the entire batch. It doesn't always happen: in our tests such a keyword simply had no data while the rest of the batch did. The Actor reports such batches in the status message and in `SUMMARY.lookupsWithoutData`, and charges no lookup fee for them. Run the sensitive keywords separately from the rest.

**Why do two similar keywords show the same volume?**
Google Ads groups close variants (plurals, word order, spelling variants) and reports the combined volume for each of them. This is how Keyword Planner works.

**Why do the numbers differ from my Keyword Planner?**
Keyword Planner shows ranges unless you run ads, and its numbers depend on the location, language, network (search partners) and date range you pick. This Actor returns the values Google Ads reports through its API (single numbers, not ranges) for the last 12 months on Google Search by default.

**Is CPC in my currency?**
CPC and bids are in USD.

**Can I check volumes for a city or state?**
Yes. Use the DataForSEO / Google Ads location code of the city, region or state, or its full name, for example `London,England,United Kingdom`.

**Does it give keyword difficulty or SERP data?**
No. This Actor focuses on Google Ads search volume, CPC, competition and bids. Competition here means advertiser competition in paid search, not SEO difficulty.

**What happens when my spending limit is reached?**
The Actor doesn't start a lookup that your remaining limit can't pay for (the $0.10 fee plus at least 10 rows). If the limit is reached while saving a lookup's rows, it keeps the best rows of that lookup (by your sort order). Either way the run finishes successfully and the status message says how many keywords were not checked.

**In which order are the rows saved?**
Rows are saved right after each lookup, sorted by your **Sort results by** choice within that lookup: your keywords first (in blocks of up to 1,000), then the keyword ideas. For lists of up to 1,000 keywords without ideas, the whole dataset is in one sorted order. To sort a bigger dataset as a whole, sort it after export (CSV, Excel) or in your code.

### Legal

The Actor returns aggregated keyword statistics from Google Ads, obtained through the DataForSEO API. It does not log in to your accounts, scrape search results or collect personal data. Search volumes and bids are estimates provided by Google and can differ from real traffic and costs. You are responsible for how you use the data and for complying with the terms and laws that apply to you. Google Ads and Keyword Planner are trademarks of Google LLC; this Actor is not affiliated with or endorsed by Google or DataForSEO.

# Actor input Schema

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

One keyword per line, up to 20,000 per run. Duplicates are removed and keywords are lowercased (Google Ads does that too). Google Ads accepts at most 80 characters and 10 words per keyword and rejects symbols such as ! @ % \* ( ) ? and emojis: those symbols are removed, and keywords that are too long or contain a letter Google Ads refuses are skipped. Both are listed in the run summary (changedKeywords, invalidKeywords).

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

Where the searches happen. Pick a country or type: a two-letter country code (us, gb, de, in ...), a country name, a DataForSEO location code (2840 = United States; states, regions and cities have their own codes) or a full DataForSEO location name such as "London,England,United Kingdom". "worldwide" = all locations.

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

Language of the searches, as a Google Ads language code or name (en, de, fr, es, pt, ja, zh-CN, "German" ...). Leave empty to use the main language of the location (English for worldwide and unknown locations).

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

Add the monthly search volumes of the last 12 months to every keyword (field monthlySearches, oldest month first). Same price either way.

## `includeKeywordIdeas` (type: `boolean`):

Also return related keyword ideas from Google Ads (Keyword Planner style) for your first 20 keywords, each with the same metrics. Ideas are rows with source = "idea" and are charged like any other keyword.

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

Upper limit on keyword ideas added (most relevant first, after the minimum volume filter). Ideas that are already in your keyword list are skipped.

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

Only keep keywords (and ideas) with at least this average monthly search volume. Skipped keywords are not charged (the lookup fee still applies when Google Ads returned data). 0 = keep all keywords that have data.

## `includeKeywordsWithoutData` (type: `boolean`):

Google Ads has no data for some keywords (too rare, restricted or not found). Off (default): they are left out and not charged, and only counted in the run summary. On: they are added with empty metrics (hasData = false) and charged like other rows (a lookup in which no keyword has data has no lookup fee either way). Ignored when a minimum volume is set.

## `sortBy` (type: `string`):

Order of the rows in the dataset. Rows are saved after every lookup (up to 1,000 keywords each), so nothing is lost if a run stops early: your keywords come first, sorted within each block of 1,000, then the keyword ideas, sorted the same way. For volume, CPC and competition, rows without that value go last. "Input order" keeps your keywords in the order you entered them, with ideas in Google's relevance order.

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

Off (default): volumes for Google Search only, as in Keyword Planner. On: Google Search plus search partner sites.

## `cacheDays` (type: `integer`):

Search volumes are monthly figures, so if you check the same keywords (same location, language and options) again within this many days, the rows you already paid for in an earlier run are reused instead of asking Google Ads again: same price per row, no lookup fee for them. Other keywords are looked up again. Saved in a key-value store named keyword-search-volume-cache in your account. 0 = always fetch fresh data.

## Actor input object example

```json
{
  "keywords": [
    "buy laptop",
    "cheap laptops for sale",
    "purchase laptop"
  ],
  "location": "us",
  "includeMonthlySearches": true,
  "includeKeywordIdeas": false,
  "maxIdeas": 100,
  "minVolume": 0,
  "includeKeywordsWithoutData": false,
  "sortBy": "searchVolume",
  "searchPartners": false,
  "cacheDays": 7
}
```

# Actor output Schema

## `keywords` (type: `string`):

All keyword rows from this run as JSON.

## `summary` (type: `string`):

Counts (keywords checked, with and without data, below the minimum volume, ideas, items saved, lookups charged), skipped and changed keywords, batches without data or rejected, cache use and any error.

# 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": [
        "buy laptop",
        "cheap laptops for sale",
        "purchase laptop",
        "gaming laptop",
        "laptop deals"
    ],
    "location": "us"
};

// Run the Actor and wait for it to finish
const run = await client.actor("fairfetch/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": [
        "buy laptop",
        "cheap laptops for sale",
        "purchase laptop",
        "gaming laptop",
        "laptop deals",
    ],
    "location": "us",
}

# Run the Actor and wait for it to finish
run = client.actor("fairfetch/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": [
    "buy laptop",
    "cheap laptops for sale",
    "purchase laptop",
    "gaming laptop",
    "laptop deals"
  ],
  "location": "us"
}' |
apify call fairfetch/keyword-search-volume --silent --output-dataset

```

## MCP server setup

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