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: Full snapshot on subscribe, then incremental diffs per block, with authoritative replacement snapshots possible at any time
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 a JSON-encodeddatastring containingorder_statuses(full order status objects matching the Orders stream) andbook_diffs(incremental changes usingraw_book_diffformat matching the Book Updates stream) - Apply diffs to your local copy of the snapshot to maintain current state
- Replacement snapshots: The stream can send another
L4BookSnapshotafter the initial one, for example when an ALO priority fee insertion changes queue order or when stream state is rebuilt. Treat every snapshot as authoritative: discard both sides of the local book, rebuild frombidsandasksin the order emitted, and continue applying later diffs from that snapshot'sheight
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 and replacements)
The initial L4BookSnapshot contains the full order book. Later snapshots use the same shape and fully replace the local 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" - Outcome markets (HIP-4):
#Nformat. Each outcome has two#Ncoins — one per side (Yes and No). Multi-price question markets consist of multiple grouped outcomes (price buckets plus a fallback), each with its own Yes/No coin pair. Use theoutcomeMetaendpoint to map coin indices to market names and sides. - 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, on reconnect, and as a mid-stream replacement reset) |
| diff | L4BookDiff | Incremental diff (sent per block between snapshots) |
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\":[...],\"book_diffs\":[...]}"
}
}
The data field is a JSON-encoded string. When parsed, it contains:
order_statuses — Full order status events, one per order changed this block. Each item matches the Orders stream event shape:
{
"order": {
"coin": "ETH",
"side": "B",
"limitPx": "3167.4",
"sz": "1.5785",
"oid": 258166296856,
"timestamp": 1764867590000,
"triggerCondition": "N/A",
"isTrigger": false,
"triggerPx": "0.0",
"isPositionTpsl": false,
"reduceOnly": false,
"orderType": "Limit",
"tif": "Gtc",
"cloid": "0x...",
"user": null
},
"status": "filled",
"time": "2025-12-04T17:00:00.518000000",
"user": "0x1c1c270b573d55b68b3d14722b5d5d401511bed0"
}
book_diffs — Incremental order book changes. Each item uses the same raw_book_diff format as the Book Updates stream:
// New order or size update
{
"coin": "ETH",
"oid": 258166296857,
"px": "3168.0",
"raw_book_diff": { "new": { "sz": "2.0000" } },
"side": "A",
"user": "0xe9acfdc9322f6f924f007016c082e6891a3c653c"
}
// Order removed (cancelled or filled)
{
"coin": "ETH",
"oid": 258166296856,
"px": "3167.4",
"raw_book_diff": "remove",
"side": "B",
"user": "0x1c1c270b573d55b68b3d14722b5d5d401511bed0"
}
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
- Treat every snapshot as authoritative: Snapshots arrive on subscribe and reconnect, and a replacement snapshot can also arrive after normal incremental updates (for example when an ALO priority fee insertion changes queue order). On active markets, replacement snapshots can arrive multiple times per minute. Whenever a snapshot arrives, discard the local book and rebuild it from the snapshot.
- Queue priority arrives via snapshots, not
insertBefore: The raw Book Updates stream exposes aninsertBeforefield on priority insertions; L4 diffs do not carry it. The L4 streams instead deliver queue-order changes through replacement snapshots whosebidsandasksare emitted in canonical queue order. - Allow large messages: Replacement snapshots contain the full L4 depth and can be much larger than an incremental diff, especially for BTC. Allow at least 100 MB for inbound gRPC messages and apply each snapshot atomically before processing later updates.
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: Between snapshots, apply each
L4BookDiffto maintain the current book. Eachbook_diffsitem usesraw_book_diff: {"new": {"sz": "..."}}to add/update an order, orraw_book_diff: "remove"to remove one, the same format as the Book Updates stream. - Typed alternative available: If you only need per-order add/update/remove movement without parsing the JSON-encoded
datastring, StreamL4BookUpdates delivers the same order-level changes as typed protobuf diffs. - 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
- StreamL4BookUpdates - Typed per-order add/update/remove diffs, no JSON parsing required
- 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