Checkpoint Streams
Part of the gRPC Gateway: Checkpoint Streams extends the standard
SubscribeCheckpointssubscription. Everything on this page works with stock Sui gRPC clients — no custom SDK required.
Overview
A stock Sui node's SubscriptionService/SubscribeCheckpoints only streams checkpoints from the moment you connect. Inodra Checkpoint Streams lets you start that same stream from any checkpoint in history — genesis, last month, or five minutes ago — and follow it seamlessly all the way to the live tip and beyond, on a single connection.
Think of it as a Kafka-style "consume from offset," for Sui checkpoints.
Same fields, historical and live
Historical checkpoints return the same field set as the live tip for a given read_mask — structured fields and transactions.balance_changes included. The stream is consistent end to end; the field set does not change as you cross from historical to live. (Two rare exceptions track the chain itself, not a limitation of history: for sponsored / multi-agent transactions the order of signatures may differ from a live read, and the top-level checkpoint signature — the validator certificate — can take more than one valid form.)
Your chosen start live tip
│ │
─────────┼──────── full history ──────────┼──── live ──►
└──────────── one gRPC stream ───┴───────────►Quick Start
Add one header to a standard SubscribeCheckpoints call:
grpcurl -H "x-api-key: YOUR_API_KEY" \
-H "x-inodra-start-checkpoint: 50000000" \
-d '{"read_mask":{"paths":["sequence_number","digest"]}}' \
mainnet-grpc.inodra.com:443 sui.rpc.v2.SubscriptionService/SubscribeCheckpointsThe stream begins at checkpoint 50000000 and runs forward continuously — through history, across the live boundary, and on as new checkpoints are produced. Without the header, the call behaves exactly like a normal node subscription (live tip only).
Headers
| Header | Required | Meaning |
|---|---|---|
x-inodra-start-checkpoint | Yes | Checkpoint sequence number to start from (inclusive) |
x-inodra-end-checkpoint | No | Stop after this checkpoint (inclusive); the stream then completes OK |
A bounded range (start + end) gives you a reproducible replay: same range, same checkpoints, clean completion. Requests with end < start are rejected with INVALID_ARGUMENT.
Delivery Guarantees
- Strictly ordered: checkpoints arrive as
start, start+1, start+2, …— never out of order. - Exactly-once per connection: no duplicates and no gaps within a single stream.
- Cursor on every message: each
SubscribeCheckpointsResponsecarries itscursor(the checkpoint sequence number).
Reconnecting
The server holds no session state. If your connection drops, reconnect with x-inodra-start-checkpoint set to your last processed cursor + 1:
let lastCursor = await loadCheckpointFromMyDatabase()
function connect() {
const meta = {
'x-api-key': API_KEY,
'x-inodra-start-checkpoint': String(lastCursor + 1)
}
// ... subscribe, and on each message: process it, then persist msg.cursor
}Across reconnects this gives you at-least-once delivery — make your processing idempotent on the checkpoint sequence number (most indexers already are).
Field Selection (read_mask)
The read_mask works exactly as it does on a Sui node — request only the fields you need:
{
"read_mask": {
"paths": [
"sequence_number",
"digest",
"summary.bcs.value",
"contents.bcs.value",
"transactions.transaction.bcs.value",
"transactions.effects.bcs.value",
"transactions.events.bcs.value",
"objects.objects.bcs.value"
]
}
}Good to know for historical checkpoints:
- Structured fields work historically. A mask requesting structured fields (object owners/types, transaction kinds, effects,
transactions.balance_changes, …) is honored for historical checkpoints too — they come back populated and matching the live node, not sparse. - BCS is still the fast path. If you only need the
*.bcs.valuepayloads (what most indexing frameworks decode locally), request just those — it's the lightest, highest-throughput mode.
Typical Patterns
Bootstrap an indexer from genesis
grpcurl -H "x-api-key: YOUR_API_KEY" \
-H "x-inodra-start-checkpoint: 0" \
-d '{"read_mask":{"paths":["sequence_number","contents.bcs.value","transactions.transaction.bcs.value","transactions.effects.bcs.value"]}}' \
mainnet-grpc.inodra.com:443 sui.rpc.v2.SubscriptionService/SubscribeCheckpointsOne stream takes the indexer from empty to fully synced to live-following — there is no separate "backfill phase" to orchestrate.
Replay an exact range
grpcurl -H "x-api-key: YOUR_API_KEY" \
-H "x-inodra-start-checkpoint: 86400000" \
-H "x-inodra-end-checkpoint: 86500000" \
-d '{"read_mask":{"paths":["sequence_number","digest"]}}' \
mainnet-grpc.inodra.com:443 sui.rpc.v2.SubscriptionService/SubscribeCheckpointsThe stream completes with OK after the end checkpoint — ideal for audits, reconciliation windows, and re-running a pipeline over a known range.
Throughput & Pacing
Historical replay is delivered as fast as your client consumes it, with adaptive pacing that keeps the flow continuous rather than bursty. Slow consumers are fine: gRPC flow control applies backpressure end to end, and the stream stays gap-free at whatever rate you process. There is no penalty for processing inline — the next checkpoint simply waits for you.
Errors
| Status | Meaning |
|---|---|
INVALID_ARGUMENT | Malformed header value, or end < start |
NOT_FOUND | The requested checkpoint isn't available from any source |
RESOURCE_EXHAUSTED | Concurrent stream limit reached — close idle streams or contact us to raise |
UNAVAILABLE | Transient upstream issue — reconnect from your last cursor + 1 |
INTERNAL | A corrupt archive chunk the tip-bucket fallback also couldn't recover; contact support |
UNIMPLEMENTED | Cursor streaming isn't enabled on this endpoint |
Billing
Checkpoint Streams is billed per delivered checkpoint, with historical and live-tip delivery metered separately. See pricing for current rates and plan limits.
Related
- gRPC Gateway — endpoints, authentication, TLS, server reflection
- Warp Streams — filtered real-time events/addresses/coins/objects over WebSocket/SSE (use Warp when you want filtered live data; use Checkpoint Streams when you want every checkpoint, including history)
- Webhooks — push notifications to your HTTPS endpoint