> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bloklake.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Following the head

> Keep a dataset up to date as new blocks arrive, correctly through reorgs, without wasting requests.

Once a backfill has caught up, keep running the same query on new blocks. Three things to get
right: when to poll, what to do when there's nothing new, and how to handle blocks that a reorg
replaces.

## The loop

```python theme={null}
import time
from bloklake import Bloklake

bl = Bloklake(chain="eth")
POLL_SECONDS = 12  # one Ethereum block; on Robinhood Chain (10 blocks a second) 1 s


def follow(query: dict, start: int, on_final, on_tentative, row_type: str = "logs"):
    """Hand rows that can no longer change to on_final(rows), once each. Hand the newest,
    still-changeable rows to on_tentative(rows); each call replaces the previous one."""
    frm = start  # first block not yet handed over as final
    while True:
        page = bl.query({**query, "from_block": frm})  # no to_block: up to the head
        finalized = page.meta.finalized_block
        rows = getattr(page, row_type)
        at_head = page.meta.next_block > page.meta.archive_height
        final = [r for r in rows if r["block_number"] <= finalized]
        if final:
            on_final(final)
        if at_head:  # this page covers the whole unfinalized tail
            on_tentative([r for r in rows if r["block_number"] > finalized])
        # Next time, start after what is final, but never past what this page covered.
        frm = max(frm, min(page.meta.next_block, finalized + 1))
        if at_head:
            time.sleep(POLL_SECONDS)  # caught up: wait for new blocks
```

`bl` is the [Python client](https://docs.bloklake.com/clients.md). 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.
* **`fromBlock` stays 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.

We tested this loop against a simulated chain with a moving head, a lagging finalized block,
4-block pages and a reorg of the tail: every final block was delivered exactly once, in its
final version.

On Robinhood Chain the unfinalized tail is 64 blocks (about 7 seconds); on Ethereum it's about 13
minutes of blocks. Re-reading it every poll costs little: usually a few hundred rows.

## When there's nothing new

A request starting past the head returns no rows and `next_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

| Chain | Block time | Sensible poll interval |
| - | - | - |
| Ethereum | 12 s | 6–12 s |
| Robinhood Chain | 100 ms | 1–2 s (each poll then carries 10–20 blocks) |

Polling faster than blocks arrive returns mostly empty responses. Each one still counts toward
your [rate limit](https://docs.bloklake.com/production/rate-limits.md) and is billed the
[10 KB minimum](https://docs.bloklake.com/billing/how-billing-works.md#minimum-per-request). 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](https://docs.bloklake.com/billing/estimating-costs.md).

> [!WARNING]
>
> On pay-as-you-go, the 1 request-per-second limit is shared by **all** your requests on **all**
> chains. A head-follower polling two chains every second leaves nothing for anything else. Poll
> less often, or use a subscription (10 requests per second).

## Only final data

If your consumers can't handle replaced rows, only hand over final rows: call `on_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's `to_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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.