StreamRawBookDiffs - Real-time Book Diff Streaming
Stream incremental order book changes from Hyperliquid L1 Gateway via gRPC. Reconstruct the resting book from per-block additions, updates, and removals.
Stream incremental order book changes starting from a position, providing real-time access to every addition, size change, and removal in Hyperliquid's resting book.
Full Code Examples
Clone our gRPC Code Examples Repository for complete, runnable implementations in Go, Python, and Node.js.
When to Use This Method
StreamRawBookDiffs is essential for:
- Order Book Reconstruction - Maintain a live local book by applying per-block diffs
- Liquidity Analysis - Track depth, spread, and resting-order churn over time
- Microstructure Research - Study how the book evolves block by block
- Backtesting Inputs - Replay historical book changes from a chosen position
Method signature
rpc StreamRawBookDiffs(StreamRequest) returns (stream Record) {}V3 accepts an inclusive position.block_number or Unix-millisecond position.timestamp. Explicit zero is invalid. A positioned call replays inclusively and then tails live; an empty request tails newly written diffs.
position: {
block_number: 1058646974
}
filter: {
fields: {
field: "coins"
op: FILTER_OP_IN
values: "BTC"
values: "ETH"
}
}Raw book diffs accept users and coins filters. User matching is case-insensitive and coin matching is case-sensitive; values within a field use OR semantics and separate fields use AND semantics. Records with zero matching events are skipped. Each Record contains optional block_number and timestamp cursors plus the filtered envelope in data. A pruned position returns NOT_FOUND; malformed filters return INVALID_ARGUMENT.
Raw diffs are not an initial book state. Seed reconstruction from a snapshot or another known state, then apply ordered diffs.
Response Stream
V3 carries the JSON-encoded node_raw_book_diffs_by_block envelope in Record.data.
Decode the data field to access:
- Block reference (
local_time,block_time,block_number) - An
eventsarray of incremental resting-book changes - Per-change order context (owner, order ID, coin, side, price)
Full Book Diff Spec
StreamRawBookDiffs emits a JSON payload matching Hyperliquid's node_raw_book_diffs_by_block format. Below is the exact structure.
Top-level keys (always present):
{
"local_time": "2026-04-20T00:00:00.040479928", // ISO-8601 timestamp when node processed the block (nanosecond precision)
"block_time": "2026-04-19T23:59:59.576040791", // ISO-8601 timestamp from block consensus
"block_number": 12345, // Block height containing these changes
"events": [ // Array of incremental book-change objects
{
"user": "0x1234567890abcdef1234567890abcdef12345678", // Order owner address
"oid": 12345, // Order identifier
"coin": "BTC", // Coin identifier
"side": "B", // "B" (buy) or "A" (sell/ask)
"px": "42000.0", // Price (numeric string)
"raw_book_diff": "remove" // Change applied to the book (see shapes below)
},
{
"user": "0x1234567890abcdef1234567890abcdef12345678",
"oid": 12346,
"coin": "BTC",
"side": "A",
"px": "42050.0",
"raw_book_diff": { "new": { "sz": "0.5" } } // Order added with size
},
{
"user": "0x1234567890abcdef1234567890abcdef12345678",
"oid": 12347,
"coin": "BTC",
"side": "A",
"px": "42100.0",
"raw_book_diff": { "update": { "newSz": "0.4", "origSz": "1.0" } } // Resting size changed
}
]
}Book diff event fields (events[i]):
| Field | Type | Description |
|---|---|---|
user | string | Order owner address |
oid | number | Order identifier. Not reliably unique across coins. See Order Identifiers |
coin | string | Coin identifier (e.g. "BTC", "ETH") |
side | string | "B" for buy, "A" for sell |
px | string | Price (numeric string) |
raw_book_diff | string or object | The change applied to the book. One of three shapes (below) |
The raw_book_diff value takes one of three shapes:
| Shape | Meaning |
|---|---|
"remove" (string literal) | Order removed from the book |
{ "new": { "sz": string } } | Order added with the given size |
{ "update": { "newSz": string, "origSz": string } } | Resting size changed from origSz to newSz |
Guarantees and alignment:
eventscontains all book changes from the block, across all coins and users.- Messages arrive in strictly increasing
block_number, one per source block (including blocks with an emptyeventsarray). block_numberis sequential and distinct from the potentially skippingabci_block.roundconsensus value in StreamBlocks;block_timealigns withabci_block.time.
Developer tips:
- Apply diffs in order: branch on the
raw_book_diffshape to add, resize, or remove the resting order. - Key your local book on
(coin, oid), never onoidalone. See Order Identifiers. - Normalize
px,sz,newSz, andorigSz(strings) to numeric types as appropriate.
Order Identifiers
oid is not reliably unique across coins. Consumers that index or join per-order state must key on the (coin, oid) tuple, never on oid alone.
Outcome markets (HIP-4) demonstrably reuse oids across the two sides of a single market (for example #30 and #31) within one block, so a diff for an oid on one coin is not necessarily the same logical order as a diff for that oid on another coin in the same block. Keying on (coin, oid) is safe whether or not oids turn out to be globally unique elsewhere.
Common Use Cases
1. Local Book Reconstruction
class BookReconstructor:
def __init__(self):
# (coin, oid) -> {"side", "px", "sz"}
self.orders = {}
def apply_block(self, block):
for event in block.get('events', []):
key = (event['coin'], event['oid'])
diff = event['raw_book_diff']
if diff == 'remove':
self.orders.pop(key, None)
elif 'new' in diff:
self.orders[key] = {
'side': event['side'],
'px': event['px'],
'sz': diff['new']['sz'],
}
elif 'update' in diff:
order = self.orders.get(key)
if order:
order['sz'] = diff['update']['newSz']2. Streaming Diffs in Python
Open V3 with an empty StreamRequest to tail newly written records, or set StreamRequest.position.block_number for inclusive replay. Decode each Record.data envelope before applying the diffs with the reconstructor above.
Error Handling and Reconnection
Reconnect with StreamRequest.position.block_number set to the last committed V3 cursor. Replay is inclusive, so deduplicate that record before applying subsequent diffs.
Best Practices
- Connection Management: Implement robust reconnection logic with exponential backoff
- Resumable State: Follow the version-specific recovery guidance above and persist the cursor required by your selected API version
- Stable Keys: Index resting orders on
(coin, oid)to stay correct across coins - Performance: Apply diffs asynchronously to avoid blocking the stream
- Resource Cleanup: Properly close streams and connections on shutdown
Current Limitations
- Replay History: Available replay history varies by feed and endpoint. A cursor outside retained history returns
NOT_FOUND. - Backpressure: High-volume periods may require careful handling to avoid overwhelming downstream systems
Resources
- GitHub: gRPC Code Examples - Complete working examples
- GetRawBookDiffs - Point-in-time book diff retrieval
- StreamOrderStatuses - The sibling block-envelope stream for order lifecycle events
- Historical Data - Historical
node_raw_book_diffs_by_blockdownloads
Need help? Contact our support team or check the Hyperliquid gRPC documentation.
StreamOrderbookSnapshots - Real-time Order Book Streaming
Stream generated order book snapshots with individual order visibility from Hyperliquid L1 Gateway via gRPC.
StreamTwapStatuses - Real-time TWAP Status Streaming
Stream TWAP order lifecycle events from Hyperliquid L1 Gateway via gRPC. Track TWAP status changes and execution progress per block in real time.