Streams guide

Replay & backfill

Event streams can start in the past. Set a block height on the subscription and the server delivers history from that height, in block order, then hands over to live delivery with no gap and no duplicate seam.

How replay works

Replay is a request parameter, not a separate API. On the swaps stream it is start_block on the request; on the StreamData family it is start_block inside the subscription. A value of 0 (or omitting it) means “from the head”; any positive height means “from there.”

  • Strict block order. Replayed messages arrive in the same order the chain produced them, then live messages continue the sequence. Your consumer needs no special backfill mode.
  • Filters apply to history too. A replayed subscription honors the same coin/wallet filters as a live one, so backfilling one wallet's fills does not mean replaying the whole tape.
  • Catch-up is as fast as you can read. Until you reach the head, messages arrive at read speed, not chain speed. Make ingestion backpressure-aware and expect a burst.
  • Deep backfills belong in the archive. Replay shines for closing gaps measured in minutes or hours. For weeks of history, bulk-load from the historical archive (same schema, exact decimals), then replay from where your export ends.

The backfill-then-live pattern

Production consumers should persist the last block height they fully processed, and always subscribe from checkpoint + 1. The same code path then handles cold start, restart, and crash recovery. There is no separate “catch-up job.”

# Backfill-then-live: one loop handles cold start, restart, and recovery.import timeimport grpcimport hyperliquid_swaps_pb2 as pbimport hyperliquid_swaps_pb2_grpc as rpc META = (("x-api-key", "YOUR_KEY"),) def run(stub):    backoff = 1    while True:        checkpoint = load_checkpoint()          # last fully-processed height        request = pb.SwapRequest(            coins=["BTC"],            start_block=checkpoint + 1,          # replay the gap, then go live        )        try:            for msg in stub.StreamSwaps(request, metadata=META):                process(msg)                     # idempotent on (block, tid)                save_checkpoint(msg.block_height)                backoff = 1                      # healthy again        except grpc.RpcError as err:            if err.code() == grpc.StatusCode.DATA_LOSS:                # We fell behind the stream buffer. The checkpoint is intact,                # so the next iteration replays exactly what we missed.                continue            time.sleep(backoff)                  # transient: backoff and retry            backoff = min(backoff * 2, 30)

Two details make this loop safe. First, process() must be idempotent: replay starts at a block boundary, so a message you half-processed before a crash may arrive again; deduplicate on a natural key like (block_height, tid) for fills or oid + status for orders. Second, checkpoint only after durable processing, never on receipt.

DATA_LOSS: replay is the recovery path

The contract is deliberate: you are told you have a gap instead of getting one you can't see. Recovery is the loop above: reconnect and replay from your checkpoint, and the gap closes itself. If DATA_LOSS recurs, your consumer is structurally too slow: tighten filters, split markets across subscriptions, or move processing off the receive thread. Reconnect strategy and monitoring guidance live in the operations guide.

Which streams replay

Replay from a block height is supported on:

  • Trades / Swaps: Every executed fill, with the trader's wallet, realized PnL, fees, and maker/taker role.
  • L4 Order Book: The full order-by-order book: every resting order with its owner.
  • Order lifecycle: Every order-status event: open, filled, canceled, triggered, rejected.
  • Raw book diffs: Every order-book delta (new, update, modified, remove) for full reconstruction.
  • TWAP algos: Native TWAP order status and execution progress, as it fills.
  • Events: Funding payments, liquidations, deposits, withdrawals, and ledger updates.
  • Blocks: The raw Hyperliquid L1 block stream.

L2 Order Book and Dedicated BTC price are snapshot- or ticker-shaped: a fresh subscription already begins with current state (or the current price), so height-based replay does not apply. To reconstruct an order book at a past block, combine an archived snapshot with replayed raw book diffs.

Related