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

# l2BookDiff | Hyperliquid WebSocket API

> Hyperliquid l2BookDiff: subscribe to real-time L2 order book (initial snapshot + diffs) for all Hyperliquid assets over WebSocket.

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

> [card] Processing
>
> Realtime

> [!TIP]
>
> Estimate your monthly cost for this API using the [Pricing Calculator](https://goldrush.dev/docs/pricing-calculator.md?endpoint=%2Fapi-reference%2Fhyperliquid-websocket%2Fl2-book-diff).

> [!NOTE]
>
> * GoldRush-native. `l2BookDiff` is not exposed on `wss://api.hyperliquid.xyz/ws`. Pointing a client at the public endpoint with this subscription type will fail.
> * Snapshot, then diffs. The first message is always a `Snapshot`; every message thereafter is an `Updates`. Clients must seed local book state from the snapshot and apply diffs from there. On reconnect, drop local state and re-seed from the next snapshot.
> * `coin` is optional. Omit it to stream the entire L2 order book across every asset on a single subscription.
> * When `coin` is omitted, the optional `marketTypes` filter selects which market families to include. It defaults to `["perp"]` only.
> * Pass `"marketTypes": ["spot"]`, or `"marketTypes":["outcome"]`, or a mix (e.g. `"marketTypes": ["perp","spot"]`), or use the wildcard `"marketTypes": ["*"]` to opt into spot, perps, outcome, and any future market types.
> * No 1000-subscription-per-IP cap - multiplex hundreds of `l2BookDiff` subscriptions on a single connection.
> * For OHLCV candles instead of raw book state, use the [Streaming API OHLCV streams](https://goldrush.dev/docs/api-reference/streaming-api/subscriptions/ohlcv-pairs-stream).

**Note:** When `coin` is omitted, a credit rate of `10` **credits per minute subscribed** is applied.

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

**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 listed coin gets its own initial `Snapshot`; subsequent `Updates` may bundle diffs for any subset of the list per block.
* **Omitted** - wildcard. Streams the full L2 book across **every perp asset** by default on one subscription (see `marketTypes` to include spot or outcome markets). The server emits one `Snapshot` per live coin, then per-block `Updates` covering only the coins that changed.

**marketTypes** `string[]`

Optional. Selects which market families a wildcard subscription includes. **Only valid when `coin` is omitted.** **Defaults to `["perp"]`** - when omitted, only perp markets stream. Spot and outcome markets require explicit opt-in.

Accepted values:

* `"perp"` *(default)* - vanilla perps and HIP-3 deployer-perps (e.g. `BTC`, `ETH`, `HYPE`, `SOL`, `cash`, `abcd`, `USA500`).
* `"spot"` - `@<index>` spot markets and legacy spot pairs (e.g. `@1`, `@107` for HYPE spot, `PURR/USDC`). Not included by default.
* `"outcome"` - HIP-4 prediction-market outcomes (e.g. `#700`, `#710`, `#741`). Not included by default.
* `"*"` - every current type (`"perp"` + `"spot"` + `"outcome"`) and auto-opt-in to any future types the server adds.

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

### Example

Pick the subscription shape that matches the coverage you want. Every subscription starts with one `Snapshot` per coin in scope, then per-block `Updates` carrying only changed levels:

| Subscribe with | What you receive |
| - | - |
| `{"type":"l2BookDiff","coin":"HYPE"}` | Snapshot + diffs for **HYPE only** |
| `{"type":"l2BookDiff","coin":["HYPE","BTC","ETH"]}` | Snapshot + diffs for **a fixed list of coins** |
| `{"type":"l2BookDiff"}` | Snapshot + diffs for **every perp coin** (default: `marketTypes: ["perp"]`) |
| `{"type":"l2BookDiff","marketTypes":["spot"]}` | Snapshot + diffs for **every spot coin** |
| `{"type":"l2BookDiff","marketTypes":["outcome"]}` | Snapshot + diffs for **every HIP-4 outcome market** |
| `{"type":"l2BookDiff","marketTypes":["perp","spot"]}` | Snapshot + diffs for **perps + spot** |
| `{"type":"l2BookDiff","marketTypes":["*"]}` | Snapshot + 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":"l2BookDiff","coin":"HYPE"}}
```

```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: "l2BookDiff", coin: "HYPE" },
  }));
});

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

  if (msg.data.Snapshot) {
    const { coin, time, block_height, levels: [bids, asks] } = msg.data.Snapshot;
    console.log("snapshot", coin, time, block_height, "bids:", bids.length, "asks:", asks.length);
  } else if (msg.data.Updates) {
    const { time, block_height, book_diffs } = msg.data.Updates;
    console.log("updates", time, block_height, "coins:", book_diffs.length);
  }
});
```

```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": "l2BookDiff", "coin": "HYPE"},
        }))
        async for raw in ws:
            msg = json.loads(raw)
            if msg.get("channel") != "l2BookDiff":
                continue
            data = msg["data"]
            if "Snapshot" in data:
                snap = data["Snapshot"]
                bids, asks = snap["levels"]
                print("snapshot", snap["coin"], snap["time"], snap["block_height"], "bids:", len(bids), "asks:", len(asks))
            elif "Updates" in data:
                upd = data["Updates"]
                print("updates", upd["time"], upd["block_height"], "coins:", len(upd["book_diffs"]))

asyncio.run(main())
```

## Unsubscribe

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

```json theme={null}
{
  "method": "unsubscribe",
  "subscription": { "type": "l2BookDiff", "coin": "HYPE" }
}
```

> [!NOTE]
>
> Unsubscribe matches subscriptions by **exact body**. A subscription created with `coin: ["BTC", "ETH"]` is a different subscription from one created with `coin: "BTC"` or `coin: "ETH"`.
>
> **You cannot unsubscribe a partial set of coins from an existing multi-coin subscription.** To narrow the set, unsubscribe the original `coin` array in full, then resubscribe with the smaller list:
>
> ```json theme={null}
> // Drop ETH from a ["BTC","ETH"] subscription:
> { "method": "unsubscribe", "subscription": { "type": "l2BookDiff", "coin": ["BTC", "ETH"] } }
> { "method": "subscribe",   "subscription": { "type": "l2BookDiff", "coin": ["BTC"] } }
> ```

## Streamed messages

Every message has `channel: "l2BookDiff"`. The `data` payload contains **exactly one** of two variants: a `Snapshot` (emitted once per subscribed coin, immediately after subscribe) or an `Updates` (emitted on each subsequent HyperCore block where the book for at least one subscribed coin changed).

### Initial snapshot

After subscribe, the server emits one `Snapshot` message per coin currently in scope. For a single-coin subscription that is one message; for a list or wildcard subscription that is one message per asset. Each entry in `levels[0]` (bids) and `levels[1]` (asks) is an aggregated price level, sorted best-first.

```json theme={null}
{
  "channel": "l2BookDiff",
  "data": {
    "Snapshot": {
      "coin": "HYPE",
      "time": 1779220051027,
      "block_height": 1002862373,
      "levels": [
        [
          { "px": "48.601", "sz": "51.26", "n": 1 }
        ],
        [
          { "px": "48.614", "sz": "12.34", "n": 1 }
        ]
      ]
    }
  }
}
```

### Incremental updates

Subsequent messages carry only the levels that changed since the previous block, grouped by coin. A level entry with `sz: "0"` and `n: 0` means **the level at that price has been removed**; any other entry replaces the current state at that `px` with the new `{sz, n}`.

```json theme={null}
{
  "channel": "l2BookDiff",
  "data": {
    "Updates": {
      "time": 1779220051224,
      "block_height": 1002862374,
      "book_diffs": [
        {
          "coin": "HYPE",
          "levels": [
            [
              { "px": "48.601", "sz": "60.00", "n": 2 }
            ],
            [
              { "px": "48.614", "sz": "0", "n": 0 }
            ]
          ]
        }
      ]
    }
  }
}
```

### Response fields

**channel** `string`

Always `"l2BookDiff"`.

**data** `object`

Contains exactly one of `Snapshot` or `Updates`.

**Snapshot (one per subscribed coin, emitted after subscribe)**

**coin** `string`

Asset symbol the snapshot belongs to.

**time** `int`

HyperCore block timestamp in milliseconds.

**block\_height** `int`

HyperCore block height the snapshot was taken at.

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

Tuple `[bids, asks]`. Each side is an array of aggregated **Level** objects in best-first order.

**level properties**

**px** `string`

Price for this level (decimal string).

**sz** `string`

Aggregate size resting at this level (decimal string, base units).

**n** `int`

Number of orders aggregated into this level.

**Updates (per-block diffs)**

**time** `int`

HyperCore block timestamp in milliseconds.

**block\_height** `int`

HyperCore block height.

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

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

**book\_diff fields**

**coin** `string`

Asset symbol the diff applies to.

**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] [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.

> [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)

*Last reviewed: 2026-06-16*


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