# Keyword Difficulty Checker (`sfoln/keyword-difficulty`)

Check Google keyword difficulty (0–100) in bulk for any country and language, up to 1,000 keywords per batch; only keywords that get a score are charged. $0.045 per batch ($45 per 1,000 batches) + $0.60 per 1,000 scored keywords.

- Family: SEO & search
- Categories: SEO tools, Lead generation, Developer tools
- Availability: on request (private on Apify). Email contact@sfoln.com with subject "Access request: Keyword Difficulty Checker (sfoln/keyword-difficulty)" before calling; calls fail until it is public.
- JSON: https://www.sfoln.com/actors/keyword-difficulty.json
- MCP: https://mcp.apify.com?tools=sfoln/keyword-difficulty
- REST (sync): POST https://api.apify.com/v2/actors/sfoln~keyword-difficulty/run-sync-get-dataset-items
- REST (async): POST https://api.apify.com/v2/actors/sfoln~keyword-difficulty/runs

Keyword Difficulty Checker gets the Google keyword difficulty (KD, 0–100) for thousands of keywords at once, for any country and language. Keyword difficulty estimates how hard it is to reach the Google top 10 for a keyword. It is a logarithmic score based on the backlink profiles of the pages that currently rank (DataForSEO Labs data). Use it to sort keyword lists by effort, plan content and spot easy wins, or let AI agents prioritise topics. Keywords are scored in batches of up to 1,000 per request, and you pay $0.045 per batch plus $0.60 per 1,000 keywords that actually get a score.

## Price per delivered record

| Event | Price | Per 1,000 | Charged for |
| --- | --- | --- | --- |
| Keyword batch (`keyword-difficulty-batch`) | $0.045 | $45 / 1,000 | Charged once per upstream request (batch of up to 1,000 keywords) that was processed. |
| Keyword difficulty score (`keyword-difficulty`) | $0.0006 | $0.60 / 1,000 | Charged once per keyword that received a difficulty score. |

Pay-per-event, USD, billed by Apify; no platform usage charge. Only records with status `ok` are charged. Cap a run with `maxTotalChargeUsd`. Apify free plan: up to 5 per run.

## Run

```bash
curl -X POST "https://api.apify.com/v2/actors/sfoln~keyword-difficulty/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keywords":["coffee grinder","burr coffee grinder","best espresso machine"]}'
```

MCP client configuration:

```json
{
  "mcpServers": {
    "apify": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=sfoln/keyword-difficulty"
    }
  }
}
```

Example prompt: Check the Google keyword difficulty in the US for "coffee grinder", "burr coffee grinder" and "best espresso machine" and tell me which is easiest to rank for.

## Input

Paste keywords; they are scored in batches of up to 1,000 per upstream request.

| Field | Type | Default | Meaning |
| --- | --- | --- | --- |
| `keywords` (required) | array | — | Keywords: Keywords to score, one per line (lower-cased and de-duplicated). Up to 1,000 keywords are sent per upstream request (one keyword-difficulty-batch fee per 1,000). Accounts on the Apify free plan can process up to 5 batches per run, with at most 20 keywords scored per batch. |
| `location` | string | `"2840"` | Location: DataForSEO location code (2840 = United States, 2826 = United Kingdom, 2276 = Germany) or full location name (e.g. "United Kingdom"). |
| `languageCode` | string | `"en"` | Language code: Language code, e.g. en, de, fr, es. |
| `maxKeywords` | integer | `10000` | Max keywords: Safety cap on how many keywords are processed in this run. |
| `concurrency` | integer | `3` | Concurrency: How many batches of 1,000 keywords are processed in parallel. |
| `includeRaw` | boolean | `false` | Include raw upstream response: Attach the unmodified DataForSEO item to each result record. |

Example input:

```json
{
  "keywords": [
    "coffee grinder",
    "burr coffee grinder",
    "best espresso machine"
  ]
}
```

## Output

One record per input item with `status` (`ok` is charged; `not_found`, `error`, `skipped_*` are free) and `charged`. The run stores an `OUTPUT` summary record.

| Field | Type | Meaning |
| --- | --- | --- |
| `recordType` | string | Record type: keyword = one keyword score; batch = summary of one batch of up to 1,000 keywords. |
| `keyword` | string | Keyword: Keyword (lower-cased). |
| `keywordDifficulty` | integer | null | Keyword difficulty: Organic ranking difficulty 0–100 (logarithmic): how hard it is to reach the Google top 10. |
| `locationCode` | integer | null | Location code: DataForSEO location code. |
| `languageCode` | string | null | Language: Language code. |
| `batch` | integer | Batch: Batch number (batch records only). |
| `keywordsInBatch` | integer | Keywords in batch: Batch records only. |
| `keywordsSent` | integer | Keywords sent: Keywords sent upstream (batch records only). |
| `keywordsWithDifficulty` | integer | Scored keywords: Keywords with a score, each charged keyword-difficulty (batch records only). |
| `keywordsWithoutData` | integer | Keywords without data: Batch records only. |
| `keywordsSkippedInvalid` | integer | Invalid keywords: Keywords rejected as invalid and not sent (batch records only). |
| `keywordsSkippedBudget` | integer | Keywords over budget: Keywords not scored or not delivered because the maximum cost per run was reached (batch records only). |
| `keywordsSkippedFreePlan` | integer | Keywords over the free-plan limit: Keywords beyond the free-plan limit of 20 per batch (batch records only). |
| `queryCharged` | boolean | Batch fee charged: Whether keyword-difficulty-batch was charged for this batch (batch records only). |
| `input` | string | object | null | Input: Keyword (keyword records) or {batch, keywords} (batch records). |
| `status` | string | Status: ok \| not_found \| error \| skipped_invalid \| skipped_budget \| skipped_free_plan |
| `charged` | boolean | Charged: Whether this record incurred a pay-per-event charge. |
| `chargedEvent` | string | Charged event: keyword-difficulty (keyword records). |
| `warnings` | array | Warnings: Non-fatal notes (limit reduced, budget reached...). |
| `error` | object | Error: {code, message, provider?} when status is not ok. |
| `source` | string | Source: Upstream data source. |
| `fetchedAt` | string | Fetched at: ISO timestamp. |

Example record:

```json
{
  "recordType": "keyword",
  "keyword": "burr coffee grinder",
  "keywordDifficulty": 38,
  "locationCode": 2840,
  "languageCode": "en",
  "input": "burr coffee grinder",
  "status": "ok",
  "charged": true,
  "chargedEvent": "keyword-difficulty",
  "source": "dataforseo",
  "fetchedAt": "2026-10-14T10:00:00.000Z"
}
```

## Related

- [Keyword Suggestions & Research Tool](https://www.sfoln.com/actors/keyword-suggestions.md) (`sfoln/keyword-suggestions`)
- [Related Keywords Finder](https://www.sfoln.com/actors/related-keywords.md) (`sfoln/related-keywords`)
- [Ranked Keywords Checker for Any Domain](https://www.sfoln.com/actors/ranked-keywords.md) (`sfoln/ranked-keywords`)

Publisher: SFOLN LLC, contact@sfoln.com. Catalogue: https://www.sfoln.com/catalog.json. Agent guide: https://www.sfoln.com/agents.md.
