CitationBenchTalk to Sales
Playbooks

From competitor URL to content plan in 5 minutes

Drop a competitor URL, get back an 8–12-item prioritized content plan targeting their winnable keywords, content gaps, and missing angles — ready to feed into bulk blog production.

Drop a competitor URL. Get back a content plan that targets the keywords they rank for, the topics they cover that you don't, and the angles they're missing.

OutcomeA 8–12-item content plan with prioritized briefs, ready to feed into produce.blog_post.bulk
Time~5 min to run; ~3 hours to draft the content from it
Cost~80 credits for the analysis + ~25 per blog post produced
PrereqsCompetitor's domain, your workspace ICPs + keywords already populated

What it does

1. research.competitor.add (the competitor's domain)
2. research.competitor.keywords (pull what they rank for)
3. research.competitor.backlinks (pull their backlinks)
4. research.competitor.overlap (vs your keyword library)
5. research.content_gap (which competitor topics you don't cover)
6. research.serp_gap.bulk (winnability per gap keyword)
7. produce.blog_post (planning mode) over the winnable EASY_WIN + WINNABLE_WITH_EFFORT gaps

Step 1 — Add the competitor

COMP=$(curl -sf -X POST .../v1/research/competitor \
  -H "Authorization: Bearer $CITATIONBENCH_API_KEY" \
  -H "X-Workspace-Id: $WS" \
  -d '{
    "domain": "monday.com",
    "weight": "incumbent"
  }' | jq -r '.id')

Step 2 — Pull their intelligence

curl -X POST .../v1/research/competitor/$COMP/keywords  -d '{"topPosition":20,"minVolume":100}'
curl -X POST .../v1/research/competitor/$COMP/backlinks -d '{"minDomainRating":30,"linkType":"dofollow"}'

Both return invocation handles; wait for SUCCEEDED.

Step 3 — Get the overlap matrix

curl .../v1/research/competitor/$COMP/overlap

Returns summary.shared, summary.competitorOnly, summary.ourOnly, plus topGaps (keywords they rank for that you don't) and topWins (the opposite).

Step 4 — Content-gap analysis across multiple competitors

curl -X POST .../v1/research/content-gap -d "{
  \"competitorIds\": [\"$COMP\"],
  \"options\": {
    \"includeWeakerCoverage\": true,
    \"maxGapsPerType\":         25,
    \"minKeywordVolume\":       100
  }
}"

When complete, fetch the report:

curl .../v1/research/content-gap/gap_***

Returns ranked gaps with type: MISSING_TOPIC | KEYWORD_GAP | WEAKER_COVERAGE, recommended angles, estimated traffic lift.

Step 5 — Filter gaps by winnability

# Get keyword IDs for KEYWORD_GAP items
GAP_KW_IDS=$(curl -sf .../v1/research/content-gap/gap_*** \
  | jq -r '.result.gaps[] | select(.type=="KEYWORD_GAP") | .keyword' \
  | head -25 \
  | while read KW; do
      # Look up or create the keyword
      curl -sf -X POST .../v1/keywords -d "{\"keywords\":[{\"keyword\":\"$KW\"}]}" \
        | jq -r '.created[0].id'
    done | jq -Rsc 'split("\n")[:-1]')

# Run winnability check
curl -X POST .../v1/research/serp-gap/bulk -d "{
  \"keywordIds\": $GAP_KW_IDS
}"

Filter to verdict ∈ {EASY_WIN, WINNABLE_WITH_EFFORT}.

Step 6 — Queue content from the winnable gaps

curl -X POST .../v1/research/content-gap/gap_***/queue-content -d '{
  "selectGaps": [
    { "type": "MISSING_TOPIC",    "priority": ["HIGH"] },
    { "type": "WEAKER_COVERAGE",  "priority": ["HIGH"] }
  ],
  "blogPostDefaults": {
    "pillarSlug": "performance",
    "mode":       "with-research",
    "refinerIds": ["rfn_brand-voice", "rfn_seo-cleanup"]
  }
}'

Returns a batch of queued blog posts at WAITING_APPROVAL.

One-shot script

#!/usr/bin/env bash
set -euo pipefail
KEY="${CITATIONBENCH_API_KEY:?}"
WS="${WORKSPACE_ID:?}"
COMPETITOR_DOMAIN="${1:?usage: $0 <competitor-domain>}"
BASE="https://api.citationbench.com/v1"

# 1. Add + pull
COMP=$(curl -sf -X POST $BASE/research/competitor \
  -H "Authorization: Bearer $KEY" -H "X-Workspace-Id: $WS" \
  -d "{\"domain\":\"$COMPETITOR_DOMAIN\"}" | jq -r '.id')

curl -sf -X POST $BASE/research/competitor/$COMP/keywords \
  -H "Authorization: Bearer $KEY" -H "X-Workspace-Id: $WS"
curl -sf -X POST $BASE/research/competitor/$COMP/backlinks \
  -H "Authorization: Bearer $KEY" -H "X-Workspace-Id: $WS"

# (allow time for completion)
sleep 90

# 2. Content gap
GAP=$(curl -sf -X POST $BASE/research/content-gap \
  -H "Authorization: Bearer $KEY" -H "X-Workspace-Id: $WS" \
  -d "{\"competitorIds\":[\"$COMP\"]}" | jq -r '.invocationId')

# 3. Wait
while true; do
  S=$(curl -sf $BASE/agent/invocations/$GAP -H "Authorization: Bearer $KEY" | jq -r '.status')
  [[ "$S" == "SUCCEEDED" ]] && break
  [[ "$S" =~ ^(FAILED|CANCELLED)$ ]] && { echo "Gap analysis $S"; exit 1; }
  sleep 30
done

REPORT_ID=$(curl -sf $BASE/agent/invocations/$GAP -H "Authorization: Bearer $KEY" \
  | jq -r '.result.reportId')

# 4. Queue HIGH-priority content
curl -sf -X POST $BASE/research/content-gap/$REPORT_ID/queue-content \
  -H "Authorization: Bearer $KEY" -H "X-Workspace-Id: $WS" \
  -d '{
    "selectGaps": [
      { "type": "MISSING_TOPIC",   "priority": ["HIGH"] },
      { "type": "WEAKER_COVERAGE", "priority": ["HIGH"] }
    ],
    "blogPostDefaults": { "mode": "with-research", "refinerIds": ["rfn_brand-voice"] }
  }'

echo "Done. Content drafts pending in your queue."

Gotchas

  • Competitor analysis is per-competitor. The deeper insight comes from analyzing 3+ competitors simultaneously. Add multiple, then content-gap across all.
  • Wait between pulls. Backlink + keyword pulls are async; running content-gap before they finish returns partial data.
  • Pillar selection matters. The default pillar shapes how the agent drafts. If your content plan spans pillars, queue content with per-gap pillar overrides.
  • Quality over volume. 8–12 well-targeted gap pieces beat 50 broad ones. Filter aggressively to high-priority gaps.

On this page