Skip to main content

L4 Order Book Dataset

Updated on
May 07, 2026

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

  1. On subscribe: The stream sends a full L4BookSnapshot containing every resting bid and ask with complete order details
  2. Per block thereafter: The stream sends L4BookDiff messages with a JSON-encoded data string containing order_statuses (full order status objects matching the Orders stream) and book_diffs (incremental changes using raw_book_diff format matching the Book Updates stream)
  3. 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

FieldTypeRequiredDescription
coinstringYesSymbol 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): #N format. Each outcome has two #N coins — 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 the outcomeMeta endpoint 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:

FieldTypeDescription
snapshotL4BookSnapshotFull order book snapshot (sent on subscribe and reconnect)
diffL4BookDiffIncremental diff (sent per block after the initial snapshot)

L4BookSnapshot

FieldTypeDescription
coinstringSymbol (e.g., "BTC", "ETH")
timeuint64Block timestamp in milliseconds
heightuint64Block height
bidsL4Order[]All resting bid orders
asksL4Order[]All resting ask orders

L4BookDiff

FieldTypeDescription
timeuint64Block timestamp in milliseconds
heightuint64Block height
datastringJSON-encoded object containing order_statuses and book_diffs, matching the existing node data format

L4Order

FieldTypeDescription
userstringEthereum address of the order owner
coinstringTrading pair identifier (e.g., "ETH", "BTC")
sidestring"A" (Ask/Sell) or "B" (Bid/Buy)
limit_pxstringLimit price as a decimal string
szstringOrder size as a decimal string
oiduint64Unique order ID
timestampuint64When the order entered the book (milliseconds)
trigger_conditionstringTrigger condition status: "N/A", "Triggered", etc.
is_triggerboolWhether the order is a trigger/stop order
trigger_pxstringTrigger price as a decimal string
is_position_tpslboolWhether the order is a position take-profit/stop-loss
reduce_onlyboolWhether the order is reduce-only
order_typestringOrder type: "Limit", "Market", etc.
tifstring (optional)Time-in-force: "Gtc" (Good til Cancelled), "Ioc" (Immediate or Cancel), "Alo" (Add Liquidity Only)
cloidstring (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'
};

gRPC Only

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

FeatureStreamL4BookStreamL2BookBOOK_UPDATES
GranularityIndividual ordersAggregated by price levelIndividual order-level diffs
Includes current stateYes — initial snapshotYes — every messageNo — forward-only
Needs REST bootstrapNoNoYes
Client state managementApply diffs to snapshotNoneBuild from scratch
Per-order detailsUser, oid, triggers, timestamps, tifTotal size and count onlyUser, oid, size
BandwidthHigher (full order details)Medium (capped by n_levels)Low (diffs only)
Best forHFT, quant desks, MEVMost customersCustomers 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_LOSS reconnection: The stream may emit gRPC DATA_LOSS status errors during transient issues. Implement auto-reconnect logic on DATA_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 L4BookDiff to maintain the current book. Each book_diffs item uses raw_book_diff: {"new": {"sz": "..."}} to add/update an order, or raw_book_diff: "remove" to remove one — the same format as the Book Updates stream.
  • 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.

  • 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
Share this doc