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

# WebSocket replay

> Replay historical 0xArchive WebSocket streams for backtesting, order-book reconstruction, incident review, and deterministic recovery.

Replay stored Hyperliquid and Lighter market data over the WebSocket, in the order it happened, for backtests, local order-book rebuilds, and incident review. A replay request reads the stored records available when the request starts, emits the requested window, and completes with `replay_completed`. It does not continue following newly arriving events.

Standard replay paces events by `speed`. L4 and full-depth replay instead uses checkpoint-anchored bulk delivery and ignores `speed`. Check [live and replay availability](/websocket/channels#channel-reference) before you choose a channel.

## Replay one channel

This example replays the five minutes that ended one hour ago, a window every plan's history reaches, including Free's most recent 30 days. Replaying older history needs the full retained archive of Build or above.

```javascript theme={"theme":"github-dark"}
const end = Date.now() - 60 * 60_000; // one hour ago
const start = end - 5 * 60_000;       // five minutes earlier

ws.send(JSON.stringify({
  op: "replay",
  channel: "orderbook",
  symbol: "BTC",
  start,
  end,
  speed: 10
}));
```

`start` and `end` are Unix milliseconds. `end` is optional and defaults to now; `replay_started` reports the effective window and speed.

## Replay several channels

Send a `channels` list instead of `channel` to replay several channels for one symbol on a single timeline. A multi-channel replay begins with a `replay_snapshot` for each channel, then interleaves events in timestamp order.

```javascript theme={"theme":"github-dark"}
const end = Date.now() - 60 * 60_000; // one hour ago
const start = end - 5 * 60_000;       // five minutes earlier

ws.send(JSON.stringify({
  op: "replay",
  channels: ["orderbook", "trades", "funding"],
  symbol: "BTC",
  start,
  end,
  speed: 10
}));
```

Keep every channel in one venue family. Do not combine Hyperliquid core channels with Lighter, HIP-3, HIP-4, or Spot channels in one command. The two Lighter deployments are separate replay families too: `rh_lighter_*` channels replay together, never with `lighter_*` channels. L4 and full-depth replay is single-channel.

```javascript theme={"theme":"github-dark"}
const end = Date.now() - 60 * 60_000; // one hour ago
const start = end - 5 * 60_000;       // five minutes earlier

ws.send(JSON.stringify({
  op: "replay",
  channels: ["rh_lighter_orderbook", "rh_lighter_funding"],
  symbol: "BTC",
  start,
  end,
  speed: 10
}));
```

## L4 and full-depth replay

L4 replay is available on `l4_diffs` and `l4_orders`, and works the same way on the Spot, HIP-3, and HIP-4 L4 channels (`spot_l4_diffs`, `spot_l4_orders`, `hip3_l4_diffs`, `hip3_l4_orders`, `hip4_l4_diffs`, `hip4_l4_orders`) and on the full-depth `orderbook_full` and `hip3_orderbook_full` channels. Each run is single-channel.

```javascript theme={"theme":"github-dark"}
const end = Date.now() - 5 * 60_000; // five minutes ago
const start = end - 2_000;           // two seconds earlier

ws.send(JSON.stringify({
  op: "replay",
  channel: "l4_orders",
  symbol: "BTC",
  start,
  end
}));
```

The server anchors the run to the latest checkpoint at or before `start`, sends an `l4_snapshot`, then sends one or more `l4_batch` events for the window: pages of up to 5,000 order events in block order on the L4 channels, and price-level changes (`side`, `px`, `sz`, `n`, `bn`) in 100 ms windows of event time on the full-depth channels. Spot frames use the dashed pair symbol. For this bulk path, `speed` is ignored.

Apply each batch in order after the snapshot to keep a local book current; a consumer that only needs one point in time can use the snapshot directly. The server collects, backfills, and anchors the data, but your client still applies the events. Bound the window and process batches off the message callback instead of using standard replay speed as backpressure.

A request can be refused when no usable checkpoint exists for the symbol and window. Checkpoints start at 2026-03-11 01:03 UTC on Hyperliquid core and HIP-3 (full-depth channels included) and 2026-05-02 07:47 UTC on HIP-4. Spot checkpoints start at 2026-05-05 22:57 UTC for most pairs; `PURR-USDC` reaches back to 2026-03-11 01:03 UTC and `HYPE-USDC` starts at 2026-05-13 15:33 UTC. Treat the accepted replay events as the authority rather than assuming one L4 start date for every symbol.

## Lighter replay

All six Lighter channels support historical replay, and Lighter on Robinhood Chain replays on its own five channels: `rh_lighter_orderbook`, `rh_lighter_trades`, `rh_lighter_candles`, `rh_lighter_open_interest`, and `rh_lighter_funding`. On Robinhood Chain, trades and candles replay from 2026-06-26 20:10 UTC; order books, open interest, and funding from 2026-08-22 18:43 UTC.

Lighter replay returns `historical_data` rows in the stored Lighter shape, which differs from the live `data` payloads. Parse the two separately, or connect with `version=2026-10-01` to receive replay rows in the live shapes. Trade replay on both deployments returns finalized trades only, up to `meta.finalized_through`. [Lighter channels](/websocket/lighter#replay) has a replay example for every channel.

## Replay controls

Pause, resume, and stop work for both standard replay and L4 replay. `replay.seek` is standard-only and is rejected for L4 and full-depth replay, which also never use `speed` for pacing.

<CardGroup cols={2}>
  <Card title="Pause" icon="pause">
    `{"op":"replay.pause"}`
  </Card>

  <Card title="Resume" icon="play">
    `{"op":"replay.resume"}`
  </Card>

  <Card title="Seek" icon="skip-forward">
    `{"op":"replay.seek","timestamp":TIMESTAMP}`
  </Card>

  <Card title="Stop" icon="square">
    `{"op":"replay.stop"}`
  </Card>
</CardGroup>

For `replay.seek`, `TIMESTAMP` is a Unix-millisecond time inside the replay window, such as `start + 60_000`.

## Replay events

Control commands use dotted `op` values, such as `replay.pause`. The events the server sends use underscore names, such as `replay_paused`.

| Event | Meaning and what to do |
| - | - |
| `replay_started` | The window was accepted. Start the run manifest: record channel, symbol, start, end, and `speed` for standard timed replay. For L4 and full-depth replay, record `delivery: checkpoint_anchored_bulk` and checkpoint metadata instead |
| `replay_snapshot` | Initial state for a multi-channel replay. Build the baseline before processing `historical_data`; a single-channel standard replay starts with `historical_data` |
| `historical_data` | A standard replay record in timeline order. Apply it by channel and keep its timestamp |
| `l4_snapshot` | L4 or full-depth checkpoint state. Initialize the book before applying batches |
| `l4_batch` | L4 or full-depth events for part of the window. Process the array in order, off the socket callback |
| `gap_detected` | A missing or discontinuous interval. Mark the output incomplete, stop, or rebuild from a known checkpoint; for L4, also check batch block and order continuity |
| `replay_paused` | Replay paused after `replay.pause`. Keep state; the run clock stops |
| `replay_resumed` | Replay resumed after `replay.resume`. Continue from the current timestamp |
| `replay_completed` | The window ended. Close the manifest and label the output complete, partial, or rejected |
| `replay_stopped` | Replay stopped after `replay.stop`. Close the manifest as stopped and keep the reason |

```javascript theme={"theme":"github-dark"}
ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === "gap_detected") {
    console.warn("Replay gap", msg.channel, msg.symbol, msg.gap_start, msg.gap_end);
  }
};
```

When a replay reports a gap, mark the derived output incomplete or rebuild from a safe checkpoint. Do not fill in missing market events.

## Record a run manifest

Store a manifest beside every replay output so another run can explain the result. It is your application's record, not part of the 0xArchive wire format.

| Field | Record |
| - | - |
| Surface | WebSocket replay |
| Venue family and symbol | The exact family and symbol sent to the server |
| Channels | `channel` for a single-channel run, or the `channels` list |
| Window | `start` and the requested `end`, or the effective end from `replay_started` when `end` was omitted |
| Delivery | `timed` for standard replay, or `checkpoint_anchored_bulk` for L4 and full-depth replay |
| Controls | `speed` for standard replay, plus any pause, resume, seek, or stop commands; omit `speed` for L4 |
| Baseline | `replay_snapshot` for multi-channel replay, or the `l4_snapshot` checkpoint metadata |
| Gaps and end state | Gap events, `replay_completed` or `replay_stopped`, and whether the output is complete, partial, or rejected |
| Code and output | SDK package and version or client revision, and where the output was written |

## Size and store a replay

Start with one channel and a narrow window. Widen only after the consumer keeps messages in order, writes output without a backlog, and handles gaps. Split long jobs into windows you can resume independently, and keep a checkpoint of your output between them.

Use REST history instead when you need records from a bounded window and can order them after retrieval. Use replay when the sequence itself is the requirement: maintaining a book, testing a strategy against event cadence, or reproducing a stream.

## Next step

Turn a replay run into reproducible backtest data with [Prepare data for a reproducible backtest](/guides/point-in-time-backtesting).


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