> ## 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.

# Finality and reorgs

> Which rows can still change, and how to read near the head safely.

Bloklake serves blocks up to the chain head. The newest blocks can still be replaced by a
**reorg**: the chain switches to a different version of its recent history, and blocks you
already read are replaced by others with different hashes and contents.

## Three numbers in every response

| Field | Meaning |
| - | - |
| `archive_height` | the newest block you can query right now |
| `finalized_block` | the newest block that can no longer change |
| `to_block_hash` | the hash of the response's last block, **only** when that block is above `finalized_block` |

Rows at or below `finalized_block` are final. Rows between `finalized_block` and
`archive_height` might still be replaced.

| Chain | Finalized after |
| - | - |
| Ethereum | the beacon chain finalizes the epoch, \~13 minutes |
| Robinhood Chain | 64 blocks, \~7 seconds |

## Strategy 1: only read finalized data

The simplest correct approach. Cap every range at `finalized_block`:

```python theme={null}
status = requests.get(f"{BASE}/status").json()
end = status["finalized_block"]
for tables in read_range(body, start, end):
    store(tables)          # never needs to be undone
```

Your data lags the head by the finality delay, and you never handle a reorg.

## Strategy 2: read to the head, re-check the unfinalized tail

For live data:

1. Read up to the head. Store rows at or below `finalized_block` as final.
2. Keep rows above it as **tentative**, together with the response's `to_block` and
   `to_block_hash`.
3. On the next poll, re-read from the first tentative block. If the block at the old
   `to_block` now has a different hash, a reorg replaced it: drop your tentative rows and use
   the new ones.

The complete loop, tested against a reorg, is in [Following the head](https://docs.bloklake.com/guides/follow-the-head.md).

Replacing the tentative rows on every poll is the simplest correct implementation: you never
need to diff, because you always re-read everything that could have changed.

> [!WARNING]
>
> A reorg can change blocks **without changing `archive_height`**: same height, different block.
> Don't detect reorgs by watching the height. Compare `to_block_hash`, or re-read the
> unfinalized range as above.

## No snapshot isolation

Two requests can see different heads. A block visible in one response might not exist yet, or
might have been replaced, in the next. If you need a stable, repeatable view, pin `toBlock` to a
block at or below `finalized_block`: the same request then always returns the same rows.

## Polling cadence

Poll about once per block time: every 12 seconds on Ethereum, every second or so on Robinhood
Chain. Polling faster returns mostly empty responses, which still count toward your rate limit
and are [billed at the 10 KB minimum](https://docs.bloklake.com/billing/how-billing-works.md#minimum-per-request). See
[Following the head](https://docs.bloklake.com/guides/follow-the-head.md) for a complete loop.


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