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

# l4BookUpdates | Hyperliquid WebSocket API

> Hyperliquid l4BookUpdates: subscribe to a strictly-ordered, diff-only order-level (L4) book stream with per-coin sequence numbers and a server epoch for gap detection.

> [card] Credit Cost
>
> 1 per coin per minute

> [card] Processing
>
> Realtime

> [!NOTE]
>
> * GoldRush-native. `l4BookUpdates` is not exposed on `wss://api.hyperliquid.xyz/ws`. Pointing a client at the public endpoint with this subscription type will fail.
> * **Order-level (L4) diffs.** Like [`l4Book`](https://goldrush.dev/docs/api-reference/hyperliquid-websocket/l4-book), every change is per **individual order** (`user`, `oid`, `side`, `raw_book_diff`) plus the order-lifecycle `order_statuses` for the block — not aggregated price levels.
> * **Strictly block-ordered.** Diffs are emitted synchronously in monotonic `block_height` order. This is the difference from [`l4Book`](https://goldrush.dev/docs/api-reference/hyperliquid-websocket/l4-book), whose per-block broadcast can deliver adjacent blocks out of order. The `l4Book` channel is unchanged; `l4BookUpdates` is the strictly-ordered, sequenced sibling.
> * **Diff-only — no inline snapshot.** This stream never sends a `Snapshot` frame; every message is an `Updates` batch. Seed your local book from the REST [`l4BookSnapshot`](https://goldrush.dev/docs/api-reference/hyperliquid-info/l4-book-snapshot) info method, then apply diffs on top — see [Bootstrapping & gap detection](#bootstrapping--gap-detection).
> * **Sequenced for gap detection.** Each per-coin diff carries a monotonic `seq` and the `prev_seq` it follows; each batch carries a server `epoch` and a `cursor`. This lets a client detect a dropped message or a server reset and re-bootstrap deterministically.
> * **Perps only.** No spot or prediction-market assets. `coin` is required — subscribe with a single symbol or an array of symbols; one asset per book.

**Note:** `l4BookUpdates` bills a **flat** rate per subscribed coin per minute, with **no BTC premium** (unlike [`l4Book`](https://goldrush.dev/docs/api-reference/hyperliquid-websocket/l4-book)). A multi-coin array bills the sum across its coins.

## Endpoint

```
wss://hypercore.goldrushdata.com/ws?key=<GOLDRUSH_API_KEY>
```

**key** `string` *required*

Your GoldRush API key. Passed as a query parameter at connection time - no `Authorization` header is used.

## Subscribe

Send this JSON message after the connection is established:

**method** `string` *required*

Always `"subscribe"`.

**subscription** `object` *required*

**properties**

**type** `string` *required*

Always `"l4BookUpdates"`.

**coin** `string | string[]` *required*

Asset filter. Perps only. Accepts two shapes:

* **String** - a single perp symbol (e.g. `"BTC"`, `"HYPE"`). For HIP-3 deployer-perps, include the deployer prefix.
* **Array of strings** - a fixed list of perp symbols (e.g. `["BTC", "ETH", "HYPE"]`, up to 64). Each coin is sequenced independently and billed independently.

Unlike [`l2BookDiff2`](https://goldrush.dev/docs/api-reference/hyperliquid-websocket/l2-book-diff2), there is no spot/outcome market filter — `l4BookUpdates` is perp-only.

### Example

| Subscribe with | What you receive |
| - | - |
| `{"type":"l4BookUpdates","coin":"BTC"}` | Order-level diffs for **BTC only** |
| `{"type":"l4BookUpdates","coin":["BTC","ETH","HYPE"]}` | Diffs for **a fixed list of perps** |

```bash wscat theme={null}
wscat -c "wss://hypercore.goldrushdata.com/ws?key=$GOLDRUSH_API_KEY"

> {"method":"subscribe","subscription":{"type":"l4BookUpdates","coin":"BTC"}}
```

```typescript TypeScript theme={null}
import WebSocket from "ws";

const ws = new WebSocket(
  `wss://hypercore.goldrushdata.com/ws?key=${process.env.GOLDRUSH_API_KEY}`,
);

ws.on("open", () => {
  ws.send(JSON.stringify({
    method: "subscribe",
    subscription: { type: "l4BookUpdates", coin: "BTC" },
  }));
});

ws.on("message", (raw) => {
  const msg = JSON.parse(raw.toString());
  if (msg.channel !== "l4BookUpdates") return;

  const { block_height, epoch, book_diffs } = msg.data.Updates;
  for (const c of book_diffs) {
    console.log(block_height, epoch, c.coin, "seq", c.prev_seq, "->", c.seq);
    for (const d of c.book_diffs) {
      // d.raw_book_diff is { new: { sz } } | { update: { origSz, newSz } } | "remove"
      console.log("  ", d.side, d.oid, d.px, JSON.stringify(d.raw_book_diff));
    }
  }
});
```

```python Python theme={null}
import asyncio, json, os
import websockets

async def main():
    uri = f"wss://hypercore.goldrushdata.com/ws?key={os.environ['GOLDRUSH_API_KEY']}"
    async with websockets.connect(uri, max_size=None) as ws:
        await ws.send(json.dumps({
            "method": "subscribe",
            "subscription": {"type": "l4BookUpdates", "coin": "BTC"},
        }))
        async for raw in ws:
            msg = json.loads(raw)
            if msg.get("channel") != "l4BookUpdates":
                continue
            upd = msg["data"]["Updates"]
            for c in upd["book_diffs"]:
                print(upd["block_height"], upd["epoch"], c["coin"], "seq", c["prev_seq"], "->", c["seq"])
                for d in c["book_diffs"]:
                    print("  ", d.get("side"), d["oid"], d["px"], d["raw_book_diff"])

asyncio.run(main())
```

## Unsubscribe

Send the same `subscription` body with `method: "unsubscribe"`:

```json theme={null}
{
  "method": "unsubscribe",
  "subscription": { "type": "l4BookUpdates", "coin": "BTC" }
}
```

> [!NOTE]
>
> Unsubscribe matches subscriptions by **exact body**. A subscription created with `coin: ["BTC", "ETH"]` is a different subscription from one created with `coin: "BTC"`. To narrow a multi-coin set, unsubscribe the original `coin` array in full, then resubscribe with the smaller list.

## Streamed messages

Every message has `channel: "l4BookUpdates"` and a `data` payload containing an `Updates` batch (this stream never emits a `Snapshot`). An `Updates` batch is emitted on each HyperCore block where the book for at least one subscribed coin changed. Each per-coin entry carries the block's `order_statuses` (order lifecycle events) and `book_diffs` (per-order changes) for that coin, plus its own `seq`/`prev_seq`.

```json theme={null}
{
  "channel": "l4BookUpdates",
  "data": {
    "Updates": {
      "time": 1778865761768,
      "block_height": 997719813,
      "epoch": "466bf60f-bb7a-4591-89a3-a2660234fef5",
      "cursor": "997719813:1778865761768",
      "book_diffs": [
        {
          "coin": "BTC",
          "seq": 4229978,
          "prev_seq": 4229977,
          "order_statuses": [
            {
              "time": "2026-05-15T17:22:41.768005701",
              "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b",
              "status": "open",
              "order": {
                "user": null,
                "coin": "BTC",
                "side": "B",
                "limitPx": "79242.0",
                "sz": "0.00867",
                "oid": 427632416336,
                "timestamp": 1778865761768,
                "triggerCondition": "N/A",
                "isTrigger": false,
                "triggerPx": "0.0",
                "isPositionTpsl": false,
                "reduceOnly": false,
                "orderType": "Limit",
                "tif": "Alo",
                "cloid": null
              }
            }
          ],
          "book_diffs": [
            {
              "user": "0x31ca8395cf837de08b24da3f660e77761dfb974b",
              "oid": 427632416336,
              "side": "B",
              "px": "79242.0",
              "coin": "BTC",
              "raw_book_diff": { "new": { "sz": "0.00867" } }
            }
          ]
        }
      ]
    }
  }
}
```

The `raw_book_diff` descriptor is one of:

* `{ "new": { "sz": "<size>" } }` — a newly resting order.
* `{ "update": { "origSz": "<old>", "newSz": "<new>" } }` — a resize (e.g. partial fill).
* `"remove"` — the order left the book (filled or cancelled).

## Bootstrapping & gap detection

`l4BookUpdates` is diff-only, so you build the order-level book yourself by seeding from a snapshot and applying diffs in order — the same mechanism a reference L4 client uses:

1. **Subscribe** to `l4BookUpdates` for your coin(s) and start **buffering** the incoming `Updates`.
2. **Fetch a snapshot** for each coin from the REST [`l4BookSnapshot`](https://goldrush.dev/docs/api-reference/hyperliquid-info/l4-book-snapshot) info method. Note its `height`, `epoch`, and per-coin `seq`.
3. **Discard already-applied diffs.** Drop any buffered batch whose `block_height` is **≤** the snapshot's `height` (the snapshot already includes those blocks). Equivalently, per coin, drop any diff whose `seq` **≤** the snapshot's `seq`.
4. **Verify the epoch.** Every remaining diff's `epoch` must equal the snapshot's `epoch`. A different `epoch` means the stream reset after your snapshot — re-fetch the snapshot and restart.
5. **Apply in order.** For each coin, the first diff you apply should have `prev_seq == snapshot.seq`. For each `raw_book_diff`, insert (`new`), resize (`update`), or delete (`remove`) the order by `oid`.
6. **Ongoing gap detection.** Per coin, each diff's `prev_seq` must equal the last `seq` you applied for that coin. On a mismatch (a dropped message) or an `epoch` change (a server reset), re-bootstrap that coin from step 2.

> [!TIP]
>
> The `seq`/`prev_seq` are **per coin**, tracked within the current `epoch`. Compare them per coin, not globally across the batch.

## Response fields

**channel** `string`

Always `"l4BookUpdates"`.

**data** `object`

Contains an `Updates` batch.

**Updates (per-block diffs)**

**time** `int`

HyperCore block timestamp in milliseconds.

**block\_height** `int`

HyperCore block height these diffs were produced at. Align against the snapshot's `height`.

**epoch** `string`

Server generation (UUID). Regenerated when the stream resets (e.g. a server restart); a new `epoch` means your snapshot and buffered diffs are stale, so re-bootstrap. Matches the `epoch` in the [`l4BookSnapshot`](https://goldrush.dev/docs/api-reference/hyperliquid-info/l4-book-snapshot) response.

**cursor** `string`

A per-batch position marker, formatted `"<block_height>:<time>"`. To recover from a disconnect, re-bootstrap from a fresh `l4BookSnapshot` (see [Bootstrapping & gap detection](#bootstrapping--gap-detection)) — the stream does not replay from this value.

**book\_diffs** `array\<object\>`

Per-coin entries. One entry per subscribed coin whose book changed at this block.

**per-coin entry**

**coin** `string`

Asset symbol this entry applies to.

**seq** `int`

Per-coin monotonic sequence for this entry, within the current `epoch`.

**prev\_seq** `int`

The `seq` of the previous entry for this coin. Must equal the last `seq` you applied for the coin (or the snapshot's `seq` for the first entry after a bootstrap). A mismatch signals a gap.

**order\_statuses** `array\<object\>`

Order-lifecycle events for this coin at this block.

**order\_status fields**

**time** `string`

ISO-8601 timestamp with nanosecond precision.

**user** `string`

Wallet address that owns the order.

**status** `string`

Lifecycle status (e.g. `"open"`).

**order** `Order`

The order, in the same shape as an [`l4Book`](https://goldrush.dev/docs/api-reference/hyperliquid-websocket/l4-book) snapshot entry. `user` inside this nested object is `null` because it duplicates the parent `user`.

**book\_diffs** `array\<object\>`

Per-order book changes for this coin at this block.

**book\_diff fields**

**user** `string`

Wallet address that owns the order.

**oid** `int`

Order id the diff applies to.

**side** `string`

`"B"` for bid, `"A"` for ask.

**px** `string`

Price the order rests at (decimal string).

**coin** `string`

Asset symbol.

**raw\_book\_diff** `object | string`

The change descriptor: `{ "new": { "sz" } }`, `{ "update": { "origSz", "newSz" } }`, or the string `"remove"`.

## Related endpoints

> [card] [l4Book](https://goldrush.dev/docs/api-reference/hyperliquid-websocket/l4-book.md)
>
> subscribe to GoldRush's order-level Hyperliquid book - initial snapshot of every resting order plus per-block diffs with full metadata.

> [card] [l2Book](https://goldrush.dev/docs/api-reference/hyperliquid-websocket/l2-book.md)
>
> subscribe to real-time L2 order book snapshots for all Hyperliquid assets over WebSocket.

> [card] [l2BookDiff](https://goldrush.dev/docs/api-reference/hyperliquid-websocket/l2-book-diff.md)
>
> subscribe to real-time L2 order book (initial snapshot + diffs) for all Hyperliquid assets over WebSocket.

> [card] [l2BookDiff2](https://goldrush.dev/docs/api-reference/hyperliquid-websocket/l2-book-diff2.md)
>
> subscribe to a diff-only L2 order book stream with per-coin sequence numbers and a server epoch for gap detection.

See all Hyperliquid endpoints: [Overview hub](https://goldrush.dev/docs/goldrush-hyperliquid/overview.md) · [WebSocket API reference](https://goldrush.dev/docs/goldrush-hyperliquid/websocket-api/overview.md)


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