CitationBenchTalk to Sales
API referenceProduction

Landing Page API — Pillar-Driven SEO Landing Page Generation

REST API for generating SEO landing pages via a four-step pipeline (search intent, SEO metadata, page copy, final metadata). Each step is independently regenerable and governed by workspace-level pillars.

Generate, edit, and manage SEO landing pages. Each landing page runs a four-step pipeline (search intent → SEO metadata → page copy → final metadata), with every step independently regenerable, and templates governed by landing-page pillars at the workspace level.

Conceptual overview

A landing page in CitationBench is two records:

  • LandingPage — the page itself: keyword, title, slug, search intent, SEO metadata, body, status, indexing flags
  • LandingPagePillar — the template + schema governing pages of this type. Pillars define the JSON schema for landingPageVariables and the prompt template that fills them.

You typically have 4–8 pillars per workspace (e.g., pricing, comparison, feature, industry, use-case, location). Each pillar's pages share structure but differ in content.

The 4-step pipeline:

1. Search intent analysis   → what does the searcher actually want?
2. SEO metadata             → title, description, slug, schema, primary keyword
3. Page copy                → body, filled to the pillar's template variables
4. Final metadata           → second pass on (2) using the actual body

Every step is independently regenerable. Copy is right but meta is off? Re-run step 4. Search intent shifted? Re-run 1–4. Want a fresh draft? Re-run all.

Endpoints

MethodPathPurpose
POST/v1/produce/landing-pageGenerate one landing page
POST/v1/produce/landing-page/bulkBulk generate from keyword list
GET/v1/produce/landing-pageList pages
GET/v1/produce/landing-page/{id}Get one
PATCH/v1/produce/landing-page/{id}Update (manual edits, status)
DELETE/v1/produce/landing-page/{id}Delete
POST/v1/produce/landing-page/{id}/regenerateRe-run pipeline from a specific step
CRUD/v1/produce/landing-page/pillar/{...}Manage pillars

POST /v1/produce/landing-page

Request

{
  "keywordId": "kw_***",
  "pillarSlug": "pricing",
  "model": "claude-sonnet-4-6",
  "options": {
    "crawlExistingUrl": "https://acme.com/old-pricing",
    "serpAnalysis": true,
    "icpSegmentId": "seg_***"
  }
}

Parameters

FieldTypeRequiredDefaultNotes
keywordIdstringyes (one of)Existing keyword
keywordstringyes (one of)Bare keyword (auto-creates)
briefIdstringyes (one of)A brief produced by bootstrap_brand or content_gap
pillarSlugstringyesWhich pillar's template to use
modelenumnoworkspace defaultclaude-sonnet-4-6, gpt-4o, gemini-2.5-pro
options.crawlExistingUrlstringnoUse an existing URL as input (optimization mode)
options.serpAnalysisbooleannotruePull SERP before writing
options.icpSegmentIdstringnoworkspace primaryTailor copy to a specific ICP
refinerIdsstring[]nopillar defaultRefiners auto-applied after draft

Response

{
  "landingPageId": "lp_***",
  "invocationId": "inv_***",
  "agentId": "agt_***",
  "skill": "produce.landing_page",
  "status": "RUNNING",
  "estimatedCost": { "credits": 38, "durationSeconds": 240 },
  "pipeline": [
    { "step": "search_intent", "status": "QUEUED" },
    { "step": "seo_metadata", "status": "QUEUED" },
    { "step": "page_copy", "status": "QUEUED" },
    { "step": "final_metadata", "status": "QUEUED" }
  ]
}

Final result

GET /v1/produce/landing-page/{landingPageId}:

{
  "id":              "lp_***",
  "pillarSlug":      "pricing",
  "keyword":         "project management software for engineering teams",
  "title":           "Project management software for engineering teams | Acme",
  "slug":            "project-management-software-for-engineering-teams",
  "description":     "Real-time capacity tracking and sprint planning ...",
  "url":             null,
  "isPublished":     false,
  "isGoogleIndexRan":false,
  "h1":              "Real-time PM for engineering teams",
  "category":        "pricing",
  "searchIntent": {
    "summary":      "Searcher comparing PM tools by feature; engineering-team specifics matter most",
    "userQueries":  ["capacity tracking", "sprint planning", "integrations with GitHub"]
  },
  "seoMetadata": {
    "metaTitle":       "Project management software for engineering teams | Acme",
    "metaDescription": "...",
    "schemaJsonLd":    { "@type": "WebPage", "...": "..." }
  },
  "landingPageVariables": {
    "hero":           { "headline": "...", "subheadline": "...", "ctaText": "Start free" },
    "featureBlocks":  [{ "title": "...", "body": "..." }],
    "pricingTable":   [{ "tier": "Starter", "price": "$99/mo", "features": [...] }],
    "faqs":           [{ "q": "...", "a": "..." }]
  },
  "lastInvocation": {
    "invocationId": "inv_***",
    "agentId":      "agt_***",
    "skill":        "produce.landing_page",
    "status":       "SUCCEEDED",
    "raw":          "Outlined the page around 3 feature blocks for engineering teams ...",
    "files":        ["agent-workspace/search-intent.md", "agent-workspace/draft-v1.html", "agent-output/page-vars.json"]
  }
}

POST /v1/produce/landing-page/bulk

curl -X POST .../v1/produce/landing-page/bulk -d '{
  "keywordIds":   ["kw_A", "kw_B", "kw_C", "kw_D"],
  "pillarSlug":   "pricing",
  "concurrency":  3
}'

Spawns one invocation per keyword.


POST /v1/produce/landing-page/{id}/regenerate

curl -X POST .../v1/produce/landing-page/lp_***/regenerate -d '{
  "fromStep":           "page_copy",
  "preserveRefinements":true
}'

fromStep can be search_intent, seo_metadata, page_copy, final_metadata. Steps before that are preserved.


CRUD: /v1/produce/landing-page/pillar/{...}

# List pillars
curl .../v1/produce/landing-page/pillar

# Create
curl -X POST .../v1/produce/landing-page/pillar -d '{
  "slug":          "comparison",
  "name":          "Comparison pages",
  "description":   "Side-by-side comparisons with competitors",
  "promptTemplate":"Generate a comparison ...",
  "schema":        { "type": "object", "properties": { ... } },
  "categories":    ["competitor"],
  "baseUrl":       "/compare/",
  "defaultRefinerIds": ["rfn_brand-voice"]
}'

# Update
curl -X PATCH .../v1/produce/landing-page/pillar/pil_***
curl -X DELETE .../v1/produce/landing-page/pillar/pil_***

MCP

> Generate a landing page for "project management for engineering teams" in the pricing pillar.

Claude calls produce.landing_page.create.

> Regenerate just the SEO metadata for lp_***.

Claude calls produce.landing_page.regenerate with fromStep: "seo_metadata".

> Generate 50 comparison pages from my competitor list.

Claude calls produce.landing_page.bulk_create.


Errors

StatusCodeCause
400validation_errorOne-of constraint violated
404keyword_not_found
404pillar_not_foundpillarSlug not in this workspace
422step_dependency_missingregenerate.fromStep requires the earlier steps to exist
503model_unavailable

Cost

ModeCredits
Full pipeline (4 steps)~38
Regenerate from step 3~28
Regenerate just step 4~7
Bulk per pagesame as single

Use cases (string things together)

A. 100 programmatic landing pages overnight

KW_IDS=$(curl -sf -X POST .../v1/keywords/search -d '{
  "intent":   ["PURCHASE", "SPECIFICATION"],
  "relevance":["OFFERING"],
  "minVolume":50
}' | jq -r '.data[].id' | jq -Rsc 'split("\n")[:-1]')

curl -X POST .../v1/produce/landing-page/bulk -d "{
  \"keywordIds\": $KW_IDS,
  \"pillarSlug\": \"pricing\",
  \"concurrency\":4
}"

B. Refresh-existing mode

curl -X POST .../v1/produce/landing-page -d '{
  "keyword":     "project management for engineering teams",
  "pillarSlug":  "pricing",
  "options":     { "crawlExistingUrl": "https://acme.com/old-page" }
}'

The agent reads the existing page, preserves what works, refreshes what doesn't.

C. Per-ICP variants

Generate 3 versions of the same page, one per ICP segment:

for SEG in seg_eng-leaders seg_founders seg_pm-managers; do
  curl -X POST .../v1/produce/landing-page -d "{
    \"keywordId\":  \"kw_***\",
    \"pillarSlug\": \"pricing\",
    \"options\":    { \"icpSegmentId\": \"$SEG\" }
  }"
done

D. Auto-publish + index

LP=$(curl -sf -X POST .../v1/produce/landing-page -d '{...}' | jq -r '.landingPageId')
# wait for completion via SSE...
curl -X POST .../v1/produce/publish -d "{
  \"landingPageId\":    \"$LP\",
  \"platformConfigId\": \"pfm_wordpress-main\"
}"
# Auto-fires indexing.gsc.submit if autoQueueOnPublish: true

On this page