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
Inputs
| Name | Type | Meaning |
|---|---|---|
pages | string[] | Up to 2,000 URLs a day per site; your Site’s key pages when unset. Default the Site’s key Pages. |
sitemap | string | A 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',. |
maxAge | string | A reading younger than this returns at $0 and spends none of the day’s 2,000. Default '1d'. |
Outputs
| Field | Type | What null means |
|---|---|---|
pages | PageIndex[] | 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[].url | string | Never 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[].reason | string | null | Null when indexed, or on Bing; otherwise the engine’s own wording. |
pages[].lastCrawl | string | null, in ISO 8601 | Never crawled. |
pages[].canonical | string | null | The engine has not chosen one yet, or Bing, which does not say. |
pages[].robots | 'allowed' | 'blocked' | null | The engine did not report it. |
pages[].lagDays | number, in days | Never null: how far behind the engine’s own report runs. |
pages[] | Observed | Each row carries observedAt, method and venue: reported on Google, sampled on Bing. |
On each Venue
| Venue | What comes back | Limits |
|---|---|---|
| Google Search | State, Google’s own reason, last crawl, the canonical Google chose and robots, method:. | 2,000 pages a day per Search Console property; the report runs about 2 days behind. |
| Bing | State and last crawl, read by a browser agent signed in to Bing Webmaster, method:. | 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-Keywith 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 (
indexedon the SEO System’s page), soMetric.querycan chart it; nothing is shared with other startups.- Emits
Page.indexCheckedwith{ pages }- Performed by
- 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-busyand 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
sendmode 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
Refuses when
| Check | Error | Fix | Then call |
|---|---|---|---|
| The call needs an account you have not connected, such as your Search Console property or your Meta ad account | account-not-connected | Connect the account with POST /{namespace}/, 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 property | quota-reached | Retry 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.measure | venue-unsupported | Pick 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
{
"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.
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.
Check every page in my sitemap on Google and Bing: 60 pages with the 12
new guides.{
"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 value | Always returns | What it is for |
|---|---|---|
maxCost: | Refuses with max-cost | Any priced call with a maxCost of 0.0001 is refused before it spends: the price is above the cap. |
rate-limited.test | Refuses with rate-limited | Any call naming this hostname answers 429 with Retry-After, so your retry logic runs. |
sources-busy.test | Refuses with sources-busy | Any 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.
await $.Monitor.create({
verb: 'Page.checkIndex',
input: { venues: ['google-search'] },
change: 'index-change',
every: '1d',
})Used by
- Jobs: Get indexed · Measure