Overviewโ
The StreamL4Book stream delivers the full order book at individual order granularity โ every resting order with user address, order ID, size, trigger info, and timestamps. On subscribe, the stream sends a complete snapshot of all resting orders, then incremental diffs per block.
gRPC Service: OrderBookStreaming
gRPC Method: StreamL4Book
API Availability: gRPC Streaming API only
Update Model: Initial full snapshot, then incremental diffs per block
How It Worksโ
- On subscribe: The stream sends a full
L4BookSnapshotcontaining every resting bid and ask with complete order details - Per block thereafter: The stream sends
L4BookDiffmessages with JSON-encoded incremental updates containingorder_statusesandbook_diffs - Apply diffs to your local copy of the snapshot to maintain current state
This is a significantly simpler way to create and maintain a local L4 order book view because the initial snapshot is built-in โ no REST bootstrap or race-condition stitching is needed. On reconnect, a fresh snapshot is automatically delivered.
Data Structureโ
Snapshot (first message)โ
The initial L4BookSnapshot contains the full order book:
{
"snapshot": {
"coin": "ETH",
"time": 1764867600518,
"height": 817863403,
"bids": [
{
"user": "0x1c1c270b573d55b68b3d14722b5d5d401511bed0",
"coin": "ETH",
"side": "B",
"limit_px": "3167.4",
"sz": "1.5785",
"oid": 258166296856,
"timestamp": 1764867590000,
"trigger_condition": "N/A",
"is_trigger": false,
"trigger_px": "0",
"is_position_tpsl": false,
"reduce_only": false,
"order_type": "Limit",
"tif": "Gtc"
}
],
"asks": [
{
"user": "0xe9acfdc9322f6f924f007016c082e6891a3c653c",
"coin": "ETH",
"side": "A",
"limit_px": "3168.0",
"sz": "2.0000",
"oid": 258166160909,
"timestamp": 1764867580000,
"trigger_condition": "N/A",
"is_trigger": false,
"trigger_px": "0",
"is_position_tpsl": false,
"reduce_only": false,
"order_type": "Limit",
"tif": "Gtc"
}
]
}
}
Diff (subsequent messages)โ
After the snapshot, each block produces an L4BookDiff with JSON-encoded incremental changes:
{
"diff": {
"time": 1764867601000,
"height": 817863404,
"data": "{\"order_statuses\": [...], \"book_diffs\": [...]}"
}
}
Request Parametersโ
| Field | Type | Required | Description |
|---|---|---|---|
| coin | string | Yes | Symbol to subscribe to โ perps use names (e.g., "BTC", "ETH"), spot uses @index format (e.g., "@142") |
Coin Namingโ
The coin parameter follows Hyperliquid's naming convention, which unambiguously distinguishes perpetuals from spot:
- Perpetuals: Human-readable names โ
"BTC","ETH","HYPE","SOL" - Spot tokens:
@{index}format โ"@1","@107","@142","@166" - Exception:
"PURR/USDC"is the only spot coin with a readable name
There is no overlap between formats. "BTC" always refers to the BTC perpetual; spot BTC is "@142". To discover @index mappings for spot tokens, query the meta or spotMeta info endpoints.
Response Fieldsโ
L4BookUpdateโ
Each message is a L4BookUpdate containing either a snapshot or a diff:
| Field | Type | Description |
|---|---|---|
| snapshot | L4BookSnapshot | Full order book snapshot (sent on subscribe and reconnect) |
| diff | L4BookDiff | Incremental diff (sent per block after the initial snapshot) |
L4BookSnapshotโ
| Field | Type | Description |
|---|---|---|
| coin | string | Symbol (e.g., "BTC", "ETH") |
| time | uint64 | Block timestamp in milliseconds |
| height | uint64 | Block height |
| bids | L4Order[] | All resting bid orders |
| asks | L4Order[] | All resting ask orders |
L4BookDiffโ
| Field | Type | Description |
|---|---|---|
| time | uint64 | Block timestamp in milliseconds |
| height | uint64 | Block height |
| data | string | JSON-encoded object containing order_statuses and book_diffs, matching the existing node data format |
L4Orderโ
| Field | Type | Description |
|---|---|---|
| user | string | Ethereum address of the order owner |
| coin | string | Trading pair identifier (e.g., "ETH", "BTC") |
| side | string | "A" (Ask/Sell) or "B" (Bid/Buy) |
| limit_px | string | Limit price as a decimal string |
| sz | string | Order size as a decimal string |
| oid | uint64 | Unique order ID |
| timestamp | uint64 | When the order entered the book (milliseconds) |
| trigger_condition | string | Trigger condition status: "N/A", "Triggered", etc. |
| is_trigger | bool | Whether the order is a trigger/stop order |
| trigger_px | string | Trigger price as a decimal string |
| is_position_tpsl | bool | Whether the order is a position take-profit/stop-loss |
| reduce_only | bool | Whether the order is reduce-only |
| order_type | string | Order type: "Limit", "Market", etc. |
| tif | string (optional) | Time-in-force: "Gtc" (Good til Cancelled), "Ioc" (Immediate or Cancel), "Alo" (Add Liquidity Only) |
| cloid | string (optional) | Client order ID (custom identifier set by user) |
Proto Definitionโ
StreamL4Book is defined in orderbook.proto:
service OrderBookStreaming {
rpc StreamL4Book (L4BookRequest) returns (stream L4BookUpdate);
}
message L4BookRequest {
string coin = 1;
}
message L4BookUpdate {
oneof update {
L4BookSnapshot snapshot = 1;
L4BookDiff diff = 2;
}
}
message L4BookSnapshot {
string coin = 1;
uint64 time = 2;
uint64 height = 3;
repeated L4Order bids = 4;
repeated L4Order asks = 5;
}
message L4BookDiff {
uint64 time = 1;
uint64 height = 2;
string data = 3;
}
message L4Order {
string user = 1;
string coin = 2;
string side = 3;
string limit_px = 4;
string sz = 5;
uint64 oid = 6;
uint64 timestamp = 7;
string trigger_condition = 8;
bool is_trigger = 9;
string trigger_px = 10;
bool is_position_tpsl = 11;
bool reduce_only = 12;
string order_type = 13;
optional string tif = 14;
optional string cloid = 15;
}
Example Updatesโ
L4 Snapshot (initial full state)
{
"snapshot": {
"coin": "ETH",
"time": 1764867600518,
"height": 817863403,
"bids": [
{
"user": "0x1c1c270b573d55b68b3d14722b5d5d401511bed0",
"coin": "ETH",
"side": "B",
"limit_px": "3167.4",
"sz": "1.5785",
"oid": 258166296856,
"timestamp": 1764867590000,
"trigger_condition": "N/A",
"is_trigger": false,
"trigger_px": "0",
"is_position_tpsl": false,
"reduce_only": false,
"order_type": "Limit",
"tif": "Gtc"
},
{
"user": "0x999a4b5f268a8fbf33736feff360d462ad248dbf",
"coin": "ETH",
"side": "B",
"limit_px": "3167.0",
"sz": "5.0000",
"oid": 258166123456,
"timestamp": 1764867585000,
"trigger_condition": "N/A",
"is_trigger": false,
"trigger_px": "0",
"is_position_tpsl": false,
"reduce_only": false,
"order_type": "Limit",
"tif": "Gtc",
"cloid": "0x20251204000000000000000000381433"
}
],
"asks": [
{
"user": "0xe9acfdc9322f6f924f007016c082e6891a3c653c",
"coin": "ETH",
"side": "A",
"limit_px": "3168.0",
"sz": "2.0000",
"oid": 258166160909,
"timestamp": 1764867580000,
"trigger_condition": "N/A",
"is_trigger": false,
"trigger_px": "0",
"is_position_tpsl": false,
"reduce_only": false,
"order_type": "Limit",
"tif": "Alo"
}
]
}
}
L4 Diff (incremental per-block update)
{
"diff": {
"time": 1764867601000,
"height": 817863404,
"data": "{\"order_statuses\":[{\"oid\":258166296856,\"status\":\"filled\"}],\"book_diffs\":[{\"coin\":\"ETH\",\"side\":\"B\",\"px\":\"3167.4\",\"sz\":\"0.0\",\"oid\":258166296856}]}"
}
}
The data field is a JSON string containing:
order_statuses: Order lifecycle changes (filled, canceled, etc.)book_diffs: Incremental book modifications to apply to your local snapshot
Trigger/Stop-Loss Order
{
"user": "0x7a475736bf02d67bf51b00414ab766ef4da9214d",
"coin": "BTC",
"side": "A",
"limit_px": "90000.0",
"sz": "0.5000",
"oid": 258166400000,
"timestamp": 1764867595000,
"trigger_condition": "Triggered",
"is_trigger": true,
"trigger_px": "91000.0",
"is_position_tpsl": true,
"reduce_only": true,
"order_type": "Limit",
"tif": "Gtc"
}
API Usageโ
gRPC Streaming
// Perp order book
const request = {
coin: 'ETH'
};
// Spot order book (@ index format)
const spotRequest = {
coin: '@142'
};
StreamL4Book is only available via the gRPC Streaming API (OrderBookStreaming service). It is not available through JSON-RPC or WebSocket.
Comparison: L4 Book vs L2 Book vs Book Updates Datasetโ
| Feature | StreamL4Book | StreamL2Book | BOOK_UPDATES |
|---|---|---|---|
| Granularity | Individual orders | Aggregated by price level | Individual order-level diffs |
| Includes current state | Yes โ initial snapshot | Yes โ every message | No โ forward-only |
| Needs REST bootstrap | No | No | Yes |
| Client state management | Apply diffs to snapshot | None | Build from scratch |
| Per-order details | User, oid, triggers, timestamps, tif | Total size and count only | User, oid, size |
| Bandwidth | Higher (full order details) | Medium (capped by n_levels) | Low (diffs only) |
| Best for | HFT, quant desks, MEV | Most customers | Customers who only need book updates |
Important Notesโ
- Snapshot on subscribe and reconnect: The stream always starts with a full snapshot, so reconnects are handled automatically
DATA_LOSSreconnection: The stream may emit gRPCDATA_LOSSstatus errors during transient issues. Implement auto-reconnect logic onDATA_LOSSโ a fresh snapshot is delivered on each new connection, so no manual state recovery is needed.- Apply diffs to local state: After the initial snapshot, apply each
L4BookDiffto maintain the current book. The JSON format matches the existing node data format used byBOOK_UPDATES. - zstd compression strongly recommended: L4 messages can be large due to full order details. Enable zstd compression on your gRPC channel to significantly reduce bandwidth.
Related Streamsโ
- StreamL2Book - Aggregated price levels โ simpler, lower bandwidth, no client-side state management
- Book Updates - Forward-only incremental diffs via StreamData (legacy approach)
- Orders - Order lifecycle events (open, filled, canceled, etc.)
- Trades - Executed trade data with maker/taker information