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

# l4Book | Hyperliquid WebSocket API

> Hyperliquid l4Book: subscribe to GoldRush's order-level Hyperliquid book - initial snapshot of every resting order plus per-block diffs with full metadata.

> [card] Credit Cost
>
> 3 per coin per minute (except BTC which is 60 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%2Fl4-book).

> [!NOTE]
>
> * GoldRush-native. `l4Book` is not exposed on `wss://api.hyperliquid.xyz/ws`. Pointing a client at the public endpoint with this subscription type will fail.
> * Perps only. No spot assets.
> * `coin` is required. Unlike `l2Book` and `l2BookDiff`, you cannot omit `coin` to stream every asset. Open one subscription per asset.
> * 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.
> * Per-order detail. Each entry exposes `user`, `oid`, `cloid`, `tif`, and trigger metadata - enabling queue-position reconstruction, per-trader flow attribution, and microstructure analytics that are not possible with `l2Book`.
> * See the [L4 Order Book recipe](https://goldrush.dev/docs/goldrush-hyperliquid/websocket-api/l4-book.md) for patterns to maintain book state, attribute flow by `user`, or reconstruct aggregated price levels.

**Note:** When `"coin":"BTC"` is used, a credit rate of `60` **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 `"l4Book"`.

**coin** `string` *required*

Asset symbol - e.g. `"BTC"`, `"ETH"`, `"@107"` for spot pairs. For HIP-3 markets, include the deployer prefix. **Required** - unlike `l2Book`, `l4Book` does not support wildcard subscriptions; each subscription is locked to a single asset.

### Example

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

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

ws.on("message", (raw) => {
  const msg = JSON.parse(raw.toString());
  if (msg.channel !== "l4Book") 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, order_statuses, book_diffs } = msg.data.Updates;
    console.log("updates", time, block_height, "statuses:", order_statuses.length, "diffs:", 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": "l4Book", "coin": "BTC"},
        }))
        async for raw in ws:
            msg = json.loads(raw)
            if msg.get("channel") != "l4Book":
                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"], "statuses:", len(upd["order_statuses"]), "diffs:", len(upd["book_diffs"]))

asyncio.run(main())
```

## Unsubscribe

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

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

## Streamed messages

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

### Initial snapshot

The first message after subscribe carries the full resting book at the current block. Each entry in `levels[0]` (bids) and `levels[1]` (asks) is an **individual order** - not an aggregated price level.

```json theme={null}
{
  "channel": "l4Book",
  "data": {
    "Snapshot": {
      "coin": "BTC",
      "time": 1778865761968,
      "block_height": 997719816,
      "levels": [
        [
          {
            "user": "0xa62b923a112d50d03e1e096bbd53422490dac104",
            "coin": "BTC",
            "side": "B",
            "limitPx": "79242",
            "sz": "0.74831",
            "oid": 427632406005,
            "timestamp": 1778865761305,
            "triggerCondition": "N/A",
            "isTrigger": false,
            "triggerPx": "0.0",
            "isPositionTpsl": false,
            "reduceOnly": false,
            "orderType": "Limit",
            "tif": "Alo",
            "cloid": "0x00000000000000000000019e2c8b7d66"
          }
        ],
        [
          {
            "user": "0xfcf104006bfff47695c1dc21dad3e9de1e72098e",
            "coin": "BTC",
            "side": "A",
            "limitPx": "79250",
            "sz": "0.2961",
            "oid": 427632406032,
            "timestamp": 1778865761305,
            "triggerCondition": "N/A",
            "isTrigger": false,
            "triggerPx": "0.0",
            "isPositionTpsl": false,
            "reduceOnly": false,
            "orderType": "Limit",
            "tif": "Gtc",
            "cloid": null
          }
        ]
      ]
    }
  }
}
```

### Incremental updates

Subsequent messages carry only what changed since the previous block. `order_statuses` describes order lifecycle events (`open`, etc.); `book_diffs` carries the corresponding price-level changes.

```json theme={null}
{
  "channel": "l4Book",
  "data": {
    "Updates": {
      "time": 1778865761768,
      "block_height": 997719813,
      "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,
          "px": "79242.0",
          "coin": "BTC",
          "raw_book_diff": { "new": { "sz": "0.00867" } }
        }
      ]
    }
  }
}
```

### Response fields

**channel** `string`

Always `"l4Book"`.

**data** `object`

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

**Snapshot (first message 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\<Order\>\>`

Tuple `[bids, asks]`. Each side is an array of individual **Order** objects (see below), in queue order at their respective price.

**Updates (per-block diffs)**

**time** `int`

HyperCore block timestamp in milliseconds.

**block\_height** `int`

HyperCore block height.

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

Order lifecycle events 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 a snapshot entry. `user` inside this nested object is `null` because it duplicates the parent `user`.

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

Per-order book changes at this block.

**book\_diff fields**

**user** `string`

Wallet address that owns the order.

**oid** `int`

Order id the diff applies to.

**px** `string`

Price level the diff applies to (decimal string).

**coin** `string`

Asset symbol.

**raw\_book\_diff** `object`

The change descriptor. Observed shape: `{ "new": { "sz": "<size>" } }` for a newly resting order. Other shapes may carry size deltas or cancellations - inspect the keys to discriminate.

### Order object

The **Order** type appears inside `Snapshot.levels[*][*]` and `Updates.order_statuses[*].order`.

**user** `string | null`

Wallet address that owns the order. `null` when the order is nested inside an `order_status` (the parent already carries it).

**coin** `string`

Asset symbol.

**side** `string`

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

**limitPx** `string`

Limit price (decimal string).

**sz** `string`

Resting size (decimal string, base units).

**oid** `int`

Hyperliquid order id - stable for the lifetime of the order.

**timestamp** `int`

Order-placement timestamp in HyperCore milliseconds.

**triggerCondition** `string`

Trigger condition string (e.g. `"N/A"` for plain limit orders).

**isTrigger** `boolean`

True if this is a stop / take-profit trigger order.

**triggerPx** `string`

Trigger price (decimal string, `"0.0"` for non-trigger orders).

**isPositionTpsl** `boolean`

True if this is a position-level TP/SL.

**reduceOnly** `boolean`

True if the order is flagged reduce-only.

**orderType** `string`

Hyperliquid order type (e.g. `"Limit"`).

**tif** `string`

Time-in-force (e.g. `"Alo"`, `"Gtc"`, `"Ioc"`).

**cloid** `string | null`

Client-supplied order id (hex string), or `null` if none was provided.

## 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] [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] [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-11-02*


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