CitationBenchTalk to Sales
API referenceIndexing

Google Search Console Indexing API — Submit URLs and Check Index Status

REST API to submit URLs to Google for indexing and check their current crawl/index status. Wraps the GSC Indexing API with auth handling, rate-limit smoothing, and auto-mirroring to IndexNow.

Submit URLs to Google for indexing and check their current status. Auto-fires after every publish if your workspace has it enabled — but also callable directly for any URL you want indexed.

Conceptual overview

Two things:

  1. Submit a URL — tell Google "please look at this." Useful for new posts, refreshed posts, or pages Google hasn't picked up.
  2. Check current status — is this URL indexed? Crawled? Discovered? Excluded?

You connect your Google Search Console account once (per workspace, per verified domain) and then call the two endpoints. CitationBench handles the auth, rotation, and rate-limit smoothing under the hood.

Endpoints

MethodPathPurpose
POST/v1/indexing/gsc-indexSubmit one or more URLs
GET/v1/indexing/gsc-indexGet index status for URL(s); list recent submissions
CRUD/v1/indexing/gsc-index/configConnect, configure, disconnect GSC

POST /v1/indexing/gsc-index

Submit one URL or many. The shape mirrors IndexNow for consistency — pass urls either as a single string or an array.

Request

POST /v1/indexing/gsc-index HTTP/1.1
Host: api.citationbench.com
Authorization: Bearer sk_live_***
X-Workspace-Id: ws_acme
Content-Type: application/json
Idempotency-Key: 7f3a1b18-7d8b-4f3e-9c4b-2c1a3e0a9b8f

{
  "urls": [
    "https://acme.com/blog/engineering-team-capacity-tracking",
    "https://acme.com/blog/sprint-planning-patterns"
  ],
  "alsoIndexNow": true
}

Parameters

FieldTypeRequiredDefaultNotes
urlsstring | string[]yesOne or many URLs on your verified domain
alsoIndexNowbooleannotrueMirror the submission to IndexNow (Bing, Yandex, partners)

Response

HTTP/1.1 202 Accepted
Content-Type: application/json

{
  "submitted": [
    {
      "url": "https://acme.com/blog/engineering-team-capacity-tracking",
      "submissionId": "sub_01HVZ...",
      "state": "ACCEPTED"
    },
    {
      "url": "https://acme.com/blog/sprint-planning-patterns",
      "submissionId": "sub_01HVZ...",
      "state": "ACCEPTED"
    }
  ],
  "alsoSubmittedToIndexNow": true
}

state on submission can be ACCEPTED (Google received the request) or REJECTED (URL not on a verified domain, malformed, etc.). The eventual index status comes back from the GET endpoint.


GET /v1/indexing/gsc-index

Use this endpoint two ways:

1. Check the status of a specific URL

curl -G "https://api.citationbench.com/v1/indexing/gsc-index" \
  -H "Authorization: Bearer sk_live_***" \
  --data-urlencode "url=https://acme.com/blog/engineering-team-capacity-tracking"
{
  "url": "https://acme.com/blog/engineering-team-capacity-tracking",
  "indexState": "INDEXED",
  "canonicalUrl": "https://acme.com/blog/engineering-team-capacity-tracking",
  "lastSubmittedAt": "2026-05-24T09:14:23Z",
  "lastCheckedAt": "2026-05-25T03:42:11Z",
  "submissions": [
    {
      "submissionId": "sub_***",
      "submittedAt": "2026-05-24T09:14:23Z",
      "outcome": "INDEXED"
    }
  ]
}

indexState values: INDEXED, NOT_INDEXED, DISCOVERED_NOT_INDEXED, CRAWLED_NOT_INDEXED, EXCLUDED, UNKNOWN.

2. List recent submissions

curl -G "https://api.citationbench.com/v1/indexing/gsc-index" \
  -H "Authorization: Bearer sk_live_***" \
  --data-urlencode "since=2026-05-01T00:00:00Z" \
  --data-urlencode "indexState=INDEXED,NOT_INDEXED" \
  --data-urlencode "limit=50"
{
  "data": [
    {
      "url": "https://acme.com/blog/post-a",
      "indexState": "INDEXED",
      "lastSubmittedAt": "...",
      "lastCheckedAt": "..."
    },
    {
      "url": "https://acme.com/blog/post-b",
      "indexState": "NOT_INDEXED",
      "lastSubmittedAt": "...",
      "lastCheckedAt": "..."
    }
  ],
  "nextCursor": null,
  "total": 47
}
ParamNotes
urlExact URL match
urlContainsSubstring match
indexStatecsv: INDEXED, NOT_INDEXED, DISCOVERED_NOT_INDEXED, CRAWLED_NOT_INDEXED, EXCLUDED, UNKNOWN
since / untilISO timestamps for lastSubmittedAt
limit, cursorPagination

CRUD: /v1/indexing/gsc-index/config

MethodPathPurpose
GET/v1/indexing/gsc-index/configGet current config
POST/v1/indexing/gsc-index/configConnect GSC (OAuth flow returns a redirect URL)
PATCH/v1/indexing/gsc-index/configUpdate settings
DELETE/v1/indexing/gsc-index/configDisconnect GSC

Config shape

{
  "id": "gscc_***",
  "verifiedDomains": ["acme.com", "blog.acme.com"],
  "autoQueueOnPublish": true,
  "alsoIndexNow": true,
  "paused": false,
  "connectedAt": "2026-02-01T...",
  "updatedAt": "2026-05-24T00:00:00Z"
}
  • autoQueueOnPublish: true — every produce.publish automatically submits to GSC.
  • alsoIndexNow: true — the default behavior of POST /v1/indexing/gsc-index mirrors submissions to IndexNow.
  • paused: true — pause submissions without disconnecting (useful during reorgs).

MCP

> Submit my latest 5 blog posts to Google for indexing.

Claude calls produce.blog_post.list then indexing.gsc.submit with the URLs.

> Is acme.com/blog/foo indexed?

Claude calls indexing.gsc.status with the URL.

> Show me all my pages from May that are CRAWLED_NOT_INDEXED.

Claude calls indexing.gsc.list filtered by indexState and since.


Errors

StatusCodeCause
400validation_errorURL malformed or empty
403gsc_not_connectedThe workspace has no GSC connection — POST /config to connect
403domain_not_verifiedURL is on a domain that isn't in the connected GSC's verifiedDomains
503gsc_unavailableTemporary upstream failure; retry with exponential backoff

Cost

ActionCredits
POST /v1/indexing/gsc-index (per URL)1
GET /v1/indexing/gsc-indexfree
Mirrored IndexNow submissionincluded

Auto-submit from publish doesn't double-charge; the publish credit covers the indexing request.


Use cases (string things together)

A. Auto-index everything you publish

# One-time setup
curl -X PATCH .../v1/indexing/gsc-index/config \
  -d '{ "autoQueueOnPublish": true, "alsoIndexNow": true }'

# Every publish now also submits to GSC and IndexNow
curl -X POST .../v1/produce/blog-post/bp_***/publish \
  -d '{ "platformConfigId": "pfm_wordpress-main" }'

B. Backfill indexing across your existing content library

# Find all published posts
URLS=$(curl -sf -G .../v1/produce/blog-post \
  --data-urlencode "status=PUBLISHED" \
  --data-urlencode "limit=500" \
  | jq -c '.data[].publishedUrl' | jq -s .)

# One bulk submission
curl -X POST .../v1/indexing/gsc-index -d "{ \"urls\": $URLS }"

C. Alert me when something fails to index

# Register a webhook
curl -X POST .../v1/webhooks -d '{
  "url": "https://hooks.our-portal.com/gsc",
  "events": ["indexing.url.not_indexed", "indexing.url.excluded"]
}'

D. Agency-wide indexing dashboard

curl -X POST .../v1/workspaces/bulk-action \
  -H "Authorization: Bearer sk_live_agency_***" \
  -d '{
    "action": "indexing.gsc.list",
    "workspaces": "all",
    "config": { "indexState": "NOT_INDEXED,CRAWLED_NOT_INDEXED", "since": "2026-05-01T00:00:00Z" }
  }'

Returns one block per workspace — surfaced in the agency's portfolio dashboard.


On this page