Skip to main content
API referenceChain

Indexer Lag

Last updated

GET
/api/v1/chain/indexer-lag

Fetch the block-indexing latency card. How long after a block is produced it becomes queryable here (#9620). chain_detail_blocks has carried two clocks since migration 0010 -- observed_at, the chain's own timestamp as the firehose poller read it, and synced_at, the wall clock of the sync handler that wrote the row -- and nothing had ever selected the second: the writer binds it on every row and no route, watchdog or artifact read it back. Their difference is the end-to-end age of a block at the moment it became answerable, which is the headline latency question for an API over a chain. TWO DIFFERENT NUMBERS, NAMED SEPARATELY: write_latency_ms is the distribution of synced_at - observed_at -- how long each block took to land, as min/p50/p95/p99/max/mean over the retained window (nearest-rank percentiles). head_age_ms is now - the newest observed_at: how far behind the lane is RIGHT NOW. They diverge exactly when it matters, because a stalled lane keeps a perfect write-latency distribution -- every block it did write, it wrote promptly -- while its head age climbs without bound, so serving either under the other's name would report a dead lane as healthy. THE WINDOW IS PUBLISHED because the table is pruned on a rolling basis (1,862 contiguous blocks, about 6.2 hours, measured 2026-08-05): the block range and the observed_at bounds ride on every response, so this reads as the RECENT distribution it is rather than a lifetime one. A NEGATIVE LATENCY IS SERVED AS MEASURED, never clamped to zero -- the two timestamps come from two clocks, so under block-author clock skew a block can appear to have been written before it was produced, and clamping would suppress that evidence on the one route whose whole subject is the difference between those clocks. DECLINES rather than answering on an empty table: degraded.reason no_retained_blocks with NULL measurements, because a zero-millisecond lag is the most flattering thing this route could say about a dead pipeline. Mainnet-only: the D1 hot tier is written by the mainnet firehose poller and carries no network column, the same reason every off-mainnet block ref resolves against the lakehouse instead.

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/api/v1/chain/indexer-lag"
{  "data": {    "block_count": 5000000,    "degraded": {      "detail": "example",      "reason": "no_retained_blocks"    },    "head_age_ms": 0.5,    "measured_at": "2026-06-01T00:00:00.000Z",    "schema_version": 1,    "window": {      "newest_block": 5000000,      "newest_observed_at": "2026-06-01T00:00:00.000Z",      "oldest_block": 5000000,      "oldest_observed_at": "2026-06-01T00:00:00.000Z"    },    "write_latency_ms": {      "max": 0.5,      "mean": 0.5,      "min": 0.5,      "p50": 0.5,      "p95": 0.5,      "p99": 0.5    }  },  "meta": {    "artifact_path": "example",    "cache": "short",    "contract_version": "2026-06-29.1",    "generated_at": "2026-06-01T00:00:00.000Z",    "pagination": {      "collection": "example",      "cursor": 1,      "limit": 1,      "next_cursor": 1,      "order": "asc",      "returned": 1,      "sort": "example",      "total": 1    },    "published_at": "2026-06-01T00:00:00.000Z",    "source": "live-cron-prober",    "stale_contract": {      "built_under": "example",      "live": "example"    }  },  "ok": true,  "schema_version": 1}