CitationBenchTalk to Sales
Playbooks

Generate 100 programmatic SEO landing pages overnight

Feed in a keyword list, get 100 brand-aligned landing pages back the next morning — programmatic SEO at scale using a 4-step pipeline with optional auto-publish and indexing.

A keyword list goes in. A hundred publish-ready landing pages come out. Programmatic SEO at scale, with the same 4-step pipeline (search intent → SEO metadata → page copy → final metadata) per page.

Outcome100 brand-aligned landing pages, optionally auto-published and indexed
Time~30 sec to queue; ~6 hours to complete 100 at default concurrency
Cost~40 credits per page × 100 = ~4,000 credits
PrereqsA pillar template configured, 100+ FOCUSED keywords ready, refiner set picked

What it does

For each keyword in the bulk list:
  produce.landing_page (with pillar template)
    ↓ runs the 4-step pipeline
    ↓ applies refiners
    ↓ optional: produce.publish
    ↓ optional: indexing.gsc.submit + indexing.indexnow

All running in parallel at the configured concurrency.

Step 1 — Pick a pillar

Each landing page uses a pillar's template. Common pillars for programmatic SEO:

PillarUse case
comparison"X vs Y" pages
featurePer-feature landing pages
industryPer-industry landing pages
locationPer-city/region landing pages
use-casePer-use-case landing pages

If you don't have one for this purpose:

curl -X POST .../v1/produce/landing-page/pillar -d '{
  "slug":         "industry",
  "name":         "Industry pages",
  "promptTemplate":"Generate an industry-specific landing page targeting <industry>",
  "schema":        { "type": "object", "properties": { "hero": { ... }, "industryProofPoints": { ... }, ... } },
  "baseUrl":       "/industries/",
  "defaultRefinerIds": ["rfn_brand-voice", "rfn_seo-cleanup", "rfn_og-image"]
}'

Step 2 — Filter to the keywords you want pages for

KW_IDS=$(curl -sf -X POST .../v1/keywords/search \
  -H "Authorization: Bearer $CITATIONBENCH_API_KEY" \
  -H "X-Workspace-Id: $WS" \
  -d '{
    "intent":     ["PURCHASE", "SPECIFICATION", "LOCATION"],
    "relevance":  ["OFFERING"],
    "minVolume":  50,
    "maxKd":      45,
    "missingFromContent": true,
    "limit":      100
  }' | jq -r '.data[].id' | jq -Rsc 'split("\n")[:-1]')

echo "Found $(echo $KW_IDS | jq 'length') keywords"

Step 3 — Fire the bulk job

curl -X POST .../v1/produce/landing-page/bulk \
  -H "Authorization: Bearer $CITATIONBENCH_API_KEY" \
  -H "X-Workspace-Id: $WS" \
  -d "{
    \"keywordIds\":   $KW_IDS,
    \"pillarSlug\":   \"industry\",
    \"concurrency\":  4
  }"

Returns a batchId + one invocation per keyword.

Step 4 — Watch progress

curl .../v1/produce/landing-page \
  -G --data-urlencode "batch=$BATCH" --data-urlencode "limit=100" \
  | jq '.data | group_by(.generationStatus) | map({status: .[0].generationStatus, count: length})'

Or wait for the batch-complete webhook:

curl -X POST .../v1/webhooks -d '{
  "url":    "https://hooks.our-portal.com/lp-batch",
  "events": ["produce.landing_page.batch.completed"]
}'

Step 5 — Optional: auto-publish + index

curl -X POST .../v1/agent/invoke -d '{
  "skill": "agent.scheduled",
  "input": {
    "steps": [
      {
        "action": "produce.landing-page.list",
        "config": { "batch": "<batchId>", "status": "DRAFT_READY" }
      },
      {
        "action":      "produce.publish",
        "forEachFrom": "$.previousStep.data",
        "config": {
          "contentType":      "landing_page",
          "contentId":        "{{ item.id }}",
          "platformConfigId": "pfm_wisp-pricing",
          "alsoIndex":        true
        }
      }
    ]
  }
}'

Or approve them in the dashboard one at a time if you want eyes on each.

One-shot script

#!/usr/bin/env bash
set -euo pipefail
KEY="${CITATIONBENCH_API_KEY:?}"
WS="${WORKSPACE_ID:?}"
PILLAR="${1:-industry}"
LIMIT="${2:-100}"
BASE="https://api.citationbench.com/v1"

# 1. Find the keywords
KW_IDS=$(curl -sf -X POST $BASE/keywords/search \
  -H "Authorization: Bearer $KEY" -H "X-Workspace-Id: $WS" \
  -d "{
    \"intent\":             [\"PURCHASE\", \"SPECIFICATION\", \"LOCATION\"],
    \"relevance\":          [\"OFFERING\"],
    \"missingFromContent\": true,
    \"limit\":              $LIMIT
  }" | jq -r '.data[].id' | jq -Rsc 'split("\n")[:-1]')

# 2. Fire the bulk job
curl -sf -X POST $BASE/produce/landing-page/bulk \
  -H "Authorization: Bearer $KEY" -H "X-Workspace-Id: $WS" \
  -d "{ \"keywordIds\": $KW_IDS, \"pillarSlug\": \"$PILLAR\", \"concurrency\": 4 }"

echo "Bulk landing-page generation queued."

Gotchas

  • Credit budget. 100 pages × 40 credits = 4,000 credits. Check your balance first; the bulk endpoint reserves cost up front.
  • Pillar template matters more than you think. A weak template produces 100 weak pages. Test with produce.landing_page on one or two keywords first; tune the template; then bulk.
  • Concurrency vs rate limits. Default 4 parallel is safe. Higher can throttle DataForSEO + Ahrefs.
  • De-duplication. If two keywords are near-synonyms, you'll get two similar pages. Run research.keyword.search with dedup first.
  • Don't auto-publish blindly. For the first batch, approve manually. Once you trust the template + refiner stack, auto-publish.

On this page