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

# l2BookDiff2 | Hyperliquid WebSocket API

> Hyperliquid l2BookDiff2: subscribe to a diff-only L2 order book stream with per-coin sequence numbers and a server epoch for gap detection.

> [card] Processing
>
> Realtime

> [!NOTE]
>
> * GoldRush-native. `l2BookDiff2` is not exposed on `wss://api.hyperliquid.xyz/ws`. Pointing a client at the public endpoint with this subscription type will fail.
> * **Diff-only — no inline snapshot.** Unlike [`l2BookDiff`](https://goldrush.dev/docs/api-reference/hyperliquid-websocket/l2-book-diff), this stream never sends a `Snapshot` frame. Every message is an `Updates` batch. Seed your local book from the REST [`l2BookDiffSnapshot`](https://goldrush.dev/docs/api-reference/hyperliquid-info/l2-book-diff-snapshot) info method, then apply diffs on top — see [Bootstrapping & gap detection](#bootstrapping--gap-detection) below.
> * **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.
> * `coin` is optional. Omit it to stream every asset on a single subscription; use `marketTypes` to choose which market families are included (defaults to `["perp"]`).

## 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 `"l2BookDiff2"`.

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

Asset filter. Accepts three shapes:

* **String** - a single asset symbol (e.g. `"HYPE"`, `"BTC"`, `"@107"` for spot pairs). For HIP-3 markets, include the deployer prefix.
* **Array of strings** - a fixed list of asset symbols (e.g. `["HYPE", "BTC", "ETH"]`). Each coin is sequenced independently.
* **Omitted** - wildcard. Streams diffs across **every perp asset** by default on one subscription (see `marketTypes` to include spot or outcome markets).

**marketTypes** `string[]`

Optional. Selects which market families a wildcard subscription includes. **Only valid when `coin` is omitted.** **Defaults to `["perp"]`.**

Accepted values:

* `"perp"` *(default)* - vanilla perps and HIP-3 deployer-perps.
* `"spot"` - `@<index>` spot markets and legacy spot pairs. Not included by default.
* `"outcome"` - HIP-4 prediction-market outcomes. Not included by default.
* `"*"` - every current type (`"perp"` + `"spot"` + `"outcome"`) and auto-opt-in to any future types.

Mix and match in one subscription, e.g. `["perp", "outcome"]`. A second `subscribe` with a different `marketTypes` value **replaces** the previous filter.

### Example

| Subscribe with | What you receive |
| - | - |
| `{"type":"l2BookDiff2","coin":"HYPE"}` | Diffs for **HYPE only** |
| `{"type":"l2BookDiff2","coin":["HYPE","BTC","ETH"]}` | Diffs for **a fixed list of coins** |
| `{"type":"l2BookDiff2"}` | Diffs for **every perp coin** (default: `marketTypes: ["perp"]`) |
| `{"type":"l2BookDiff2","marketTypes":["*"]}` | Diffs for **every coin** (perp + spot + outcome, plus future types) |

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

> {"method":"subscribe","subscription":{"type":"l2BookDiff2","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: "l2BookDiff2", coin: "BTC" },
  }));
});

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

  const { time, block_height, epoch, cursor, book_diffs } = msg.data.Updates;
  for (const d of book_diffs) {
    console.log(block_height, epoch, d.coin, "seq", d.prev_seq, "->", d.seq);
    // apply d.levels = [changedBids, changedAsks]; sz "0" removes the level
  }
});
```

```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) as ws:
        await ws.send(json.dumps({
            "method": "subscribe",
            "subscription": {"type": "l2BookDiff2", "coin": "BTC"},
        }))
        async for raw in ws:
            msg = json.loads(raw)
            if msg.get("channel") != "l2BookDiff2":
                continue
            upd = msg["data"]["Updates"]
            for d in upd["book_diffs"]:
                print(upd["block_height"], upd["epoch"], d["coin"], "seq", d["prev_seq"], "->", d["seq"])

asyncio.run(main())
```

## Unsubscribe

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

```json theme={null}
{
  "method": "unsubscribe",
  "subscription": { "type": "l2BookDiff2", "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: "l2BookDiff2"` 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.

```json theme={null}
{
  "channel": "l2BookDiff2",
  "data": {
    "Updates": {
      "time": 1786379817106,
      "block_height": 1105180205,
      "epoch": "466bf60f-bb7a-4591-89a3-a2660234fef5",
      "cursor": "1105180205:1786379817106",
      "book_diffs": [
        {
          "coin": "BTC",
          "seq": 4229978,
          "prev_seq": 4229977,
          "levels": [
            [
              { "px": "57686.0", "sz": "0.09903", "n": 25 }
            ],
            [
              { "px": "64164.0", "sz": "0", "n": 0 }
            ]
          ]
        }
      ]
    }
  }
}
```

Each entry in `levels[0]` (bids) and `levels[1]` (asks) replaces the current state at its `px`. A level with `sz: "0"` and `n: 0` means **the level at that price has been removed**.

## Bootstrapping & gap detection

`l2BookDiff2` gives you diffs only, so you build the book yourself by seeding from a snapshot and applying diffs in order:

1. **Subscribe** to `l2BookDiff2` for your coin(s) and start **buffering** the incoming `Updates`.
2. **Fetch a snapshot** for each coin from the REST [`l2BookDiffSnapshot`](https://goldrush.dev/docs/api-reference/hyperliquid-info/l2-book-diff-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). You may equivalently discard per coin by `seq` - drop any diff whose `seq` **≤** the snapshot's `seq` - for the tightest alignment.
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 level, upsert `{px, sz, n}`, or remove the price when `sz` is `"0"`.
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 `"l2BookDiff2"`.

**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 [`l2BookDiffSnapshot`](https://goldrush.dev/docs/api-reference/hyperliquid-info/l2-book-diff-snapshot) response.

**cursor** `string`

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

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

Per-coin lists of changed price levels. One entry per coin that changed at this block.

**book\_diff fields**

**coin** `string`

Asset symbol the diff applies to.

**seq** `int`

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

**prev\_seq** `int`

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

**levels** `array\<array\<Level\>\>`

Tuple `[changed_bids, changed_asks]`. Each entry replaces the current state at its `px`; an entry with `sz: "0"` and `n: 0` removes the level at that price.

**level properties**

**px** `string`

Price for this level (decimal string).

**sz** `string`

New aggregate size at this level (decimal string). `"0"` means the level is removed.

**n** `int`

New number of orders aggregated into this level. `0` means the level is removed.

## Related endpoints

> [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] [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] [l4BookUpdates](https://goldrush.dev/docs/api-reference/hyperliquid-websocket/l4-book-updates.md)
>
> subscribe to a strictly-ordered, diff-only order-level (L4) 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.