The loop
bl is the Python client. The same structure works in any language and with
the API directly.
How it works:
- No
toBlock: each request reads up to the current head. fromBlockstays at the first block that isn’t final yet. Every poll re-reads everything that could still change, so a reorg is handled by replacing your tentative rows with the new ones. You never diff or roll back.- Final rows are handed over once. The next start is the block after the finalized one, but never past the end of the page just read: when many new rows arrive, the loop pages through them before it waits.
- Tentative rows are reported from every page that reaches the head, even when there are none, so they always describe the whole unfinalized tail, and a reorg that removes rows clears them too.
When there’s nothing new
A request starting past the head returns no rows andnext_block equal to your fromBlock.
That’s the normal “nothing yet” answer, not an error. Wait and repeat.
Poll about once per block, not faster
Polling faster than blocks arrive returns mostly empty responses. Each one still counts toward
your rate limit and is billed the
10 KB minimum. On pay-as-you-go (1 request per
second), polling every second uses your whole rate limit and costs about 26 GB a month
(≈ $13) in minimums alone. Polling Ethereum every 12 seconds costs about 2 GB a month
(≈ $1). See Estimating costs.
Only final data
If your consumers can’t handle replaced rows, only hand over final rows: callon_final and
ignore on_tentative. Your data then lags the head by the finality delay (about 7 seconds on
Robinhood Chain, about 13 minutes on Ethereum) and never changes after you store it.
Detecting a reorg explicitly
If you want to log or count reorgs: keep the last response’sto_block and to_block_hash
(present when to_block is above finalized_block). On the next poll, read that block’s hash
again (select fields.block.hash for it). If it differs, a reorg replaced it. A reorg can
replace a block without changing the height, so never use the height alone.