CitationBenchTalk to Sales
API referenceResearch

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

MethodPathPurpose
POST/v1/research/serp-gapRun a gap analysis (one or many queries)
GET/v1/research/serp-gapList recent analyses
GET/v1/research/serp-gap/{id}Get one analysis
POST/v1/research/serp-gap/bulkBulk 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

FieldTypeRequiredDefaultNotes
querystringyes (or queries)Single query
queriesstring[]yes (or query)Up to 50 queries in one call
location / languageenumnoworkspace default
ourDomainstringnoworkspace primaryThe domain whose ranking potential to score
options.considerOwnedSubdomainsbooleannotrueTreat subdomains of ourDomain as owned
options.weightContent / weightDR / weightBrandnumbernotuned defaultsMust sum to 1
serpIdstringnoReuse 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

VerdictScore rangeMeaning
EASY_WIN85–100Ranking is highly likely with quality content
WINNABLE_WITH_EFFORT60–84Ranking is realistic but requires deep content + some link building
STRETCH35–59Possible but high effort; consider alternatives
DOMINATED0–34Top 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

StatusCodeCause
400validation_errorMissing query/queries
422serp_too_smallSERP returned fewer than 10 results — can't reliably score cliffs
503external_unavailableDataForSEO / Ahrefs down

Cost

ActionCredits
Per single query3
Per bulk query3
Reusing a serpId2 (skips refetch)
GET endpointsfree

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_EFFORT

B. 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 pursuing

C. 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_***"
  }
}'

On this page