Skip to content

Page.checkIndex

Read each page’s index state on Google and Bing, with the engine’s own reason, its last crawl and the canonical it chose, and the fix.

Price
Free
Success rate
99.9% of 53,608 calls
Freshness
Fresh for 1 day
Emits
Page.indexChecked
POST https://api.sb/{your-domain}/pages/check-index

Inputs

NameTypeMeaning
pagesstring[]Up to 2,000 URLs a day per site; your Site’s key pages when unset. Default the Site’s key Pages.
sitemapstringA sitemap URL: every page it lists is checked, in place of pages.
venues('google-search' | 'bing')[]The engines to read; each needs its account connected. Default ['google-search', 'bing'].
maxAgestringA reading younger than this returns at $0 and spends none of the day’s 2,000. Default '1d'.

Outputs

FieldTypeWhat null means
pagesPageIndex[]Never null: one row for each page and engine. What it means not-seen: The engine has never found the page. Confirm it is in the sitemap, link it from 2 indexed pages, and earn one followed outside link. Source: the index-state playbook, 2026.
discovered-not-indexed: Found, not crawled yet: low priority on a new domain. Link it from the home and pricing pages; request indexing for key pages only. Source: the index-state playbook, 2026.
crawled-not-indexed: Crawled and judged not worth indexing yet. Merge thin pages and add a fact only you have; do not request again until the page changed. Source: the index-state playbook, 2026.
Key pages indexed under 80% by week 2, or under 100% by week 4: Discovery is the problem, not content. Fix discovery before you write another page. Planning estimate.
Over 30% crawled-not-indexed at week 6: A quality problem across the site. Pause publishing and fix the thin pages first. Planning estimate.
pages[].urlstringNever null: the page as sent, or as the sitemap lists it.
pages[].state'indexed' | 'discovered-not-indexed' | 'crawled-not-indexed' | 'duplicate' | 'not-seen' | 'excluded'Never null.
pages[].reasonstring | nullNull when indexed, or on Bing; otherwise the engine’s own wording.
pages[].lastCrawlstring | null, in ISO 8601Never crawled.
pages[].canonicalstring | nullThe engine has not chosen one yet, or Bing, which does not say.
pages[].robots'allowed' | 'blocked' | nullThe engine did not report it.
pages[].lagDaysnumber, in daysNever null: how far behind the engine’s own report runs.
pages[]ObservedEach row carries observedAt, method and venue: reported on Google, sampled on Bing.

On each Venue

VenueWhat comes backLimits
Google SearchState, Google’s own reason, last crawl, the canonical Google chose and robots, method: reported.2,000 pages a day per Search Console property; the report runs about 2 days behind.
BingState and last crawl, read by a browser agent signed in to Bing Webmaster, method: sampled.No reason or canonical: Bing’s report shows neither.

Effects

Action class

None: no approval rule names it, so it runs within its seat’s Permissions and Limits

Modes
  • try: Runs every check and returns the rows; nothing is written onto the pages.
  • send: Returns a pending Action at once; its Event carries the rows when the read finishes.
  • do: Waits for the read and returns the rows, billed per result.
Idempotency

The same Idempotency-Key with the same input replays the first answer at $0; with other input it returns 409.

Saved or shared

Writes each page’s state onto its page (indexed on the SEO System’s page), so Metric.query can chart it; nothing is shared with other startups.

Emits

Page.indexChecked with { pages }

Performed by

Content lead

Price

Free. Free: every check, read from your own Search Console and Bing Webmaster.

Reliability

The first route succeeds 99.9% of 53,608 calls. If it fails, the call refuses with sources-busy and nothing is spent.

Freshness

A reading younger than 1 day returns at $0. Readings about people are never shared across startups. The source runs 2 days behind.

Batch and pages

Up to 2,000 pages a day per site, shared with every other tool on that property. Over 50 pages it answers in send mode with a pending Action at once.

Rate

The shared rate rules: 10 calls a second per key, and an array input counts as one call.

Capability

Domains

Refuses when

CheckErrorFixThen call
The call needs an account you have not connected, such as your Search Console property or your Meta ad accountaccount-not-connectedConnect the account with POST /{namespace}/integrations/connect, approve it at the connectUrl, then send the call again.Integration.connect
The account’s own daily or per-minute cap is used up, such as 2,000 page checks a day per Search Console propertyquota-reachedRetry after the reset time the response gives; nothing was sent or spent.Action.retry
A search engine, answer engine or network the call does not read, such as ChatGPT sent to Keyword.measurevenue-unsupportedPick one the call’s venue input lists; AI answers are Keyword.ask, posts are Keyword.search.Keyword.ask

Every data call can also meet the shared data-call refusals: filter-unsupported, input-missing, limit-exceeded, max-cost, rate-limited, sources-busy.

The account-not-connected payload
JSON
{
  "type": "https://startupbuilder.dev/errors/account-not-connected",
  "title": "Account not connected",
  "status": 409,
  "detail": "The call needs an account you have not connected, such as your Search Console property or your Meta ad account.",
  "fix": "Connect the account with Integration.connect, approve it at the connectUrl, then send the call again.",
  "next": {
    "verb": "Integration.connect",
    "input": {
      "system": "ads",
      "venue": "facebook",
      "scope": "startup",
      "grants": [
        "ads_management",
        "ads_read"
      ]
    }
  },
  "instance": "https://api.sb/leaseline.app/actions/action_1"
}

When it waits

A call over 50 rows returns a pending Action; its Page.indexChecked Event carries the rows; nothing needs your approval.

TypeScript
await $.Watch.create({
  selector: 'Page.indexChecked',
  url: 'https://leaseline.app/hooks/sb',
})

Example

A batch: over 50 pages, the check answers in send mode with a pending Action, and Page.indexChecked carries the rows.

You, to your agent
Check every page in my sitemap on Google and Bing: 60 pages with the 12
new guides.
Response, in send mode
{
  "mode": "send",
  "action": {
    "$id": "https://api.sb/leaseline.app/actions/action_Ix5pC2kQ8v",
    "operation": "Page.checkIndex",
    "status": "pending",
    "reason": "running"
  },
  "pages": []
}

To retry, send the same Idempotency-Key: the retry returns the first answer for $0. A new key reads again.

Test values, and watching it

A sk_test_ key answers these the same way every time, at $0 (every test value).

Test valueAlways returnsWhat it is for
maxCost: 0.0001Refuses with max-costAny priced call with a maxCost of 0.0001 is refused before it spends: the price is above the cap.
rate-limited.testRefuses with rate-limitedAny call naming this hostname answers 429 with Retry-After, so your retry logic runs.
sources-busy.testRefuses with sources-busyAny call naming this hostname answers 503 as if every route were busy, and nothing is spent.

Watch it on a schedule: a Monitor runs this call and records an Event only when the reading changes.

TypeScript
await $.Monitor.create({
  verb: 'Page.checkIndex',
  input: { venues: ['google-search'] },
  change: 'index-change',
  every: '1d',
})

Used by