> For the complete documentation index, see [llms.txt](https://docs.hydromancer.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hydromancer.xyz/readme/websocket/l4bookupdates.md).

# l4BookUpdates

{% hint style="info" %}
💧 New endpoint - this endpoint is not a part of original Hyperliquid API and is added by us for builder convenience.
{% endhint %}

### Subscribe

```json
{
    "method": "subscribe",
    "subscription": {
        "type": "l4BookUpdates",
        "coins": ["ETH", "BTC"]
    }
}
```

**Parameters:**

| Parameter     | Type      | Required | Description                                                                                                                           |
| ------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `coins`       | string\[] | No       | Coins to subscribe to. Omit for all markets (needs **l4BookUpdatesAll** add-on).                                                      |
| `marketTypes` | string\[] | No       | All-markets only. Filters delivery by market type — see [All-markets filter](#all-markets-filter). Rejected if combined with `coins`. |

### All-markets filter

When `coins` is omitted, the optional `marketTypes` field restricts the firehose to specific market types. Each entry is `"perp"`, `"spot"`, `"outcome"`, or the wildcard `"*"` (alone) for "every type the server currently tracks":

```json
{
    "method": "subscribe",
    "subscription": {
        "type": "l4BookUpdates",
        "marketTypes": ["perp", "outcome"]
    }
}
```

Omitting `marketTypes` defaults to `["perp"]` — outcome and spot markets do **not** appear unless you opt in. The default never grows; new market types must be added to your `marketTypes` array explicitly. Pass `["*"]` to auto-opt-in to future types.

A second subscribe with a different `marketTypes` value **replaces** the previous filter rather than coexisting with it.

### Unsubscribe

```json
{
    "method": "unsubscribe",
    "subscription": {
        "type": "l4BookUpdates",
        "coins": ["ETH", "BTC"]
    }
}
```

### Update data format

Updates are streamed per-block with order changes for subscribed markets:

```json
{
    "type": "l4BookUpdates",
    "seq": 1,
    "cursor": "500:1704067200000",
    "data": {
        "height": 782007304,
        "timestamp": 1704067200000,
        "diffs": [
            {
                "type": "new",
                "coin": "ETH",
                "oid": 217148811876,
                "user": "0x742d35cc6634c0532925a3b844bc9e7595f7f2e2",
                "side": "B",
                "px": "3245.50",
                "sz": "1.5"
            },
            {
                "type": "update",
                "coin": "ETH",
                "oid": 217148811800,
                "sz": "2.0"
            },
            {
                "type": "remove",
                "coin": "ETH",
                "oid": 217148811750
            }
        ]
    }
}
```

### Diff types

| Type     | Description                       | Fields                                    |
| -------- | --------------------------------- | ----------------------------------------- |
| `new`    | New order placed                  | `coin`, `oid`, `user`, `side`, `px`, `sz` |
| `update` | Order size changed (partial fill) | `coin`, `oid`, `sz`                       |
| `remove` | Order removed (filled/cancelled)  | `coin`, `oid`                             |

### Field definitions

| Field       | Type   | Description                        |
| ----------- | ------ | ---------------------------------- |
| `height`    | int    | Block height                       |
| `timestamp` | int    | Block timestamp (ms since epoch)   |
| `coin`      | string | Market symbol (e.g., "ETH", "BTC") |
| `oid`       | int    | Unique order ID                    |
| `user`      | string | User address (0x...)               |
| `side`      | string | "B" = bid, "A" = ask               |
| `px`        | string | Price as decimal string            |
| `sz`        | string | Size as decimal string             |

### Examples

{% tabs %}
{% tab title="JavaScript" %}

```javascript
const WebSocket = require('ws');

const ws = new WebSocket(`wss://api.hydromancer.xyz/ws?token=${process.env.HYDROMANCER_API_KEY}`);

ws.on('open', () => {
    ws.send(JSON.stringify({
        method: 'subscribe',
        subscription: {
            type: 'l4BookUpdates',
            coins: ['ETH', 'BTC']
        }
    }));
});

ws.on('message', (data) => {
    const msg = JSON.parse(data);

    if (msg.type === 'ping') {
        ws.send(JSON.stringify({ method: 'pong' }));
    } else if (msg.type === 'l4BookUpdates') {
        const { height, diffs } = msg.data;
        console.log(`Height ${height}: ${diffs.length} order changes`);
    }
});
```

{% endtab %}

{% tab title="Python" %}

```python
import websocket
import json
import os

def on_message(ws, message):
    msg = json.loads(message)

    if msg.get('type') == 'ping':
        ws.send(json.dumps({'method': 'pong'}))
    elif msg.get('type') == 'l4BookUpdates':
        data = msg['data']
        print(f"Height {data['height']}: {len(data['diffs'])} order changes")

def on_open(ws):
    ws.send(json.dumps({
        "method": "subscribe",
        "subscription": {
            "type": "l4BookUpdates",
            "coins": ["ETH", "BTC"]
        }
    }))

if __name__ == "__main__":
    api_key = os.environ.get('HYDROMANCER_API_KEY')
    ws = websocket.WebSocketApp(f'wss://api.hydromancer.xyz/ws?token={api_key}',
                                on_open=on_open,
                                on_message=on_message)
    ws.run_forever()
```

{% endtab %}
{% endtabs %}

### Common errors

1. `Too many coins` - Reduce number of coins or upgrade tier
2. `Subscribing to all markets requires permission` - Needs add-on for all books
3. `Rate limit exceeded` - Reduce subscription frequency
4. `Authentication failed` - Check API key
