Research · SERP Gap API — Detect SERP Cliffs & Score Keyword Winnability Before Spending Content Credits
Score whether a SERP is winnable by detecting DR, content, brand, and intent cliffs — the highest-ROI question to ask before committing content production to a keyword.
A serp gap analysis detects whether there's a winnable space in a SERP — what we call a "serp cliff." A cliff is when the top 1–3 results are dominant (high DR, strong content) and then there's a drop-off to weaker results from rank 4 onward. The space between the top tier and the weak tail is where ranking is realistic.
This is the highest-ROI question you can ask of a keyword: can I actually rank for this? Use it before committing content production credits to keywords that look attractive in keyword research but are dominated by entrenched giants.
Conceptual overview
The analysis takes one or more queries, fetches their SERPs, and scores the winnability of each by analyzing:
- DR cliff — distribution of Domain Rating across ranks 1–20. A sharp drop after rank 3 means there's space.
- Content cliff — distribution of content depth, freshness, and authority signals. A drop means content quality is winnable.
- Brand cliff — how many top results are giant brands (
monday.com,notion.so) vs. niche publishers. Giant brands are sticky; niche publishers can be displaced. - Intent saturation — is the SERP already perfectly matching searcher intent, or is there a content angle nobody's covering?
The output is a per-query winnability score, the specific cliff(s) detected, and a recommended angle for content that could break in.
Endpoints
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/research/serp-gap | Run a gap analysis (one or many queries) |
| GET | /v1/research/serp-gap | List recent analyses |
| GET | /v1/research/serp-gap/{id} | Get one analysis |
| POST | /v1/research/serp-gap/bulk | Bulk run across a keyword list |
POST /v1/research/serp-gap
Request
POST /v1/research/serp-gap HTTP/1.1
Host: api.citationbench.com
Authorization: Bearer sk_live_***
X-Workspace-Id: ws_acme
Content-Type: application/json
{
"query": "best project management software for engineering teams",
"location": "us",
"language": "en",
"ourDomain": "acme.com",
"options": {
"considerOwnedSubdomains": true,
"weightContent": 0.4,
"weightDR": 0.4,
"weightBrand": 0.2
}
}Parameters
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
query | string | yes (or queries) | — | Single query |
queries | string[] | yes (or query) | — | Up to 50 queries in one call |
location / language | enum | no | workspace default | — |
ourDomain | string | no | workspace primary | The domain whose ranking potential to score |
options.considerOwnedSubdomains | boolean | no | true | Treat subdomains of ourDomain as owned |
options.weightContent / weightDR / weightBrand | number | no | tuned defaults | Must sum to 1 |
serpId | string | no | — | Reuse a recently-fetched SERP instead of re-fetching |
Response
HTTP/1.1 202 Accepted
{
"invocationId": "inv_***",
"agentId": "agt_***",
"skill": "research.serp_gap",
"status": "RUNNING",
"estimatedCost":{ "credits": 3, "durationSeconds": 12 }
}Final result
{
"invocationId": "inv_***",
"agentId": "agt_***",
"skill": "research.serp_gap",
"status": "SUCCEEDED",
"creditsUsed": 3,
"result": {
"gapId": "gap_***",
"query": "best project management software for engineering teams",
"ourDomain": "acme.com",
"currentRank": 14,
"winnability": {
"score": 72,
"confidence": 0.83,
"verdict": "WINNABLE_WITH_EFFORT",
"estimatedMonthsToTopThree": 4
},
"cliffs": [
{
"type": "DR",
"detected": true,
"description": "Top 3 results average DR 82; results 4–20 average DR 47. Strong DR cliff after rank 3.",
"cliffAt": 3,
"topAvg": 82,
"tailAvg": 47
},
{
"type": "BRAND",
"detected": false,
"description": "Only 1 giant brand in top 10 (asana.com at rank 7). Niche publishers dominate."
},
{
"type": "CONTENT",
"detected": true,
"description": "Top 3 results average 3,200 words; results 4–20 average 1,400 words. Content depth cliff present.",
"cliffAt": 3
},
{
"type": "INTENT_SATURATION",
"detected": false,
"description": "All top 10 results cover the same 4 angles. A 5th angle (capacity forecasting for engineering teams) is unaddressed."
}
],
"recommendation": {
"shouldPursue": true,
"angle": "Lead with capacity forecasting for engineering teams — the SERP's unaddressed angle. Target 3,500+ words with original research.",
"estimatedEffort": "high",
"supportingSerpId": "srp_***"
},
"competitors": [
{
"rank": 1,
"domain": "wettletter.com",
"domainRating": 84,
"wordCount": 3120
},
{
"rank": 2,
"domain": "engineering-blog.com",
"domainRating": 62,
"wordCount": 2980
},
{
"rank": 3,
"domain": "linear.app",
"domainRating": 91,
"wordCount": 3450
}
]
},
"raw": "Analyzed the SERP. Top 3 results have strong DR + deep content, creating a cliff at rank 3. The rest of the page is winnable. I notice no top result covers capacity forecasting — that's an open angle.",
"files": [
"agent-workspace/dr-distribution.csv",
"agent-workspace/competitor-content-analysis.md",
"agent-output/winnability-report.md"
]
}Verdict values
| Verdict | Score range | Meaning |
|---|---|---|
EASY_WIN | 85–100 | Ranking is highly likely with quality content |
WINNABLE_WITH_EFFORT | 60–84 | Ranking is realistic but requires deep content + some link building |
STRETCH | 35–59 | Possible but high effort; consider alternatives |
DOMINATED | 0–34 | Top results entrenched; not worth pursuing |
POST /v1/research/serp-gap/bulk
Run gap analysis across many queries (e.g., the full output of research.keyword).
curl -X POST https://api.citationbench.com/v1/research/serp-gap/bulk \
-H "Authorization: Bearer sk_live_***" \
-d '{
"keywordIds": ["kw_A", "kw_B", "kw_C", "kw_D", "kw_E"],
"concurrency": 5
}'Response includes one invocation per query, plus a roll-up.
{
"batchId": "batch_***",
"queries": [
{ "keywordId": "kw_A", "invocationId": "inv_A", "status": "PENDING" },
{ "keywordId": "kw_B", "invocationId": "inv_B", "status": "PENDING" }
],
"totalEstimatedCost": { "credits": 25 }
}When complete, fetch GET /v1/research/serp-gap/batch/{batchId} for the aggregate.
GET /v1/research/serp-gap
curl -G "https://api.citationbench.com/v1/research/serp-gap" \
--data-urlencode "verdict=EASY_WIN,WINNABLE_WITH_EFFORT" \
--data-urlencode "since=2026-05-01T00:00:00Z" \
--data-urlencode "limit=50"Filter by verdict, score range, query, or recency.
MCP
> Run a serp gap analysis for "best project management software for engineering teams" with acme.com.Claude calls research.serp_gap.analyze.
> For my top 50 PURCHASE-intent keywords, which ones are winnable?Claude calls research.keyword.search then research.serp_gap.bulk.
Errors
| Status | Code | Cause |
|---|---|---|
| 400 | validation_error | Missing query/queries |
| 422 | serp_too_small | SERP returned fewer than 10 results — can't reliably score cliffs |
| 503 | external_unavailable | DataForSEO / Ahrefs down |
Cost
| Action | Credits |
|---|---|
| Per single query | 3 |
| Per bulk query | 3 |
Reusing a serpId | 2 (skips refetch) |
| GET endpoints | free |
Use cases (string things together)
A. Triage a new keyword universe
# After bootstrap_brand returns 1,247 keywords, filter to PURCHASE intent + check winnability
KW_IDS=$(curl -sf -X POST .../v1/keywords/search -d '{
"intent": ["PURCHASE"],
"minVolume": 200,
"limit": 50
}' | jq -r '.data[].id' | jq -Rsc 'split("\n")[:-1]')
curl -X POST .../v1/research/serp-gap/bulk -d "{ \"keywordIds\": $KW_IDS }"
# 5 minutes later: read the batch summary, focus content on EASY_WIN + WINNABLE_WITH_EFFORTB. Block content production for DOMINATED keywords
Wire produce.blog_post.create to refuse keywords with the most recent serp-gap verdict of DOMINATED:
# Eval gate
when:
field_eq: { lastSerpGapVerdict: "DOMINATED" }
then:
action: escalate_to_approval
reason: SERP is dominated; recommend not pursuingC. Refresh the cliff analysis quarterly
SERPs change. Re-run gap analysis on FOCUSED keywords every quarter to catch keywords that became more (or less) winnable.
D. Use gap analysis to feed content_factory's angle selection
The agent's content brief can include the gap analysis's recommendation.angle as the framing guidance:
curl -X POST .../v1/agent/invoke -d '{
"skill": "content_factory",
"input": {
"keywordId": "kw_***",
"serpGapAnalysisId": "gap_***"
}
}'Related
- API: Research · serp
- API: Research · keyword
- API: Production · blog post
- API: Production · landing page
- Concept: Eval Gates
- Playbook: From competitor URL to content plan
SERP
Fetch the full SERP for any query — organic results, AI Overviews, featured snippets, People Also Ask, Reddit blocks, video carousels, local pack — parsed, persisted, and diffable over time.
Competitor
Add competitors as first-class workspace resources, then pull their backlinks, ranking keywords, and keyword overlap matrices against your own library — the data layer behind gap analysis and outreach.