L2 Order Book
Aggregated price levels per coin, 1–100 levels deep. Guaranteed never crossed.
Top-of-book and depth by price level for a single coin. Subscribe with {coin, n_levels}; the first message is a full snapshot, then one update per block. Levels carry price, aggregated size, and resting-order count.
- Method
- hyperliquid.OrderBookStreaming/StreamL2Book
- Endpoint
- stream.hyperliquidrpc.com:443 · gRPC over TLS (HTTP/2)
- Auth
- API key in x-api-key metadata
- Volume
- One update per block per subscribed coin gives predictable, low-bandwidth depth. Use n_levels: 1 as a top-of-book ticker.
When to use it
This is the default book surface for pricing: market-making quote engines, spread monitors, execution algos that need depth, and any dashboard that shows a ladder. You choose the depth (n_levels, 1–100), the server does the aggregation, and every message is a consistent post-block view that is guaranteed never crossed. There are no “bid above ask” states to defend against.
If you need to know whose orders sit at each level, or your model depends on queue position, use the L4 book instead. If you only need a single price, n_levels: 1 here is a perfectly good top-of-book ticker.
Method
// stream.hyperliquidrpc.com:443 — gRPC over TLS (HTTP/2)// auth: API key in x-api-key metadata · server reflection enabledpackage hyperliquid; service OrderBookStreaming { // Full snapshot first, then one full refresh per block. Never crossed. rpc StreamL2Book (L2BookRequest) returns (stream L2BookUpdate);}This service is wire-compatible with QuickNode's order-book proto, so generated stubs work unchanged after swapping the endpoint and key.
Subscribe
# Streams JSON-encoded messages until you disconnect.grpcurl -H 'x-api-key: YOUR_KEY' \ -d '{"coin":"BTC","n_levels":20}' \ stream.hyperliquidrpc.com:443 \ hyperliquid.OrderBookStreaming/StreamL2Bookcoin is required and takes exactly one market per subscription; open one stream per coin you track. Bids and asks arrive best-price-first in levels.
Message fields
Fields typed string that carry numbers are byte-exact decimal strings, verbatim from the node. Compare and store them as strings or arbitrary-precision decimals. Never parseFloat a price you intend to reconcile.
Server-side filters
- coin: required, one market per subscription
- n_levels: 1 to 100
Filters are applied on the server, so filtered-out messages never leave our edge. You only pay for what you subscribe to. Request shapes for every stream family are on Filtering; backfill semantics on Replay & backfill.
Semantics & gotchas
Snapshot-then-updates: first message is a full book snapshot, then one full refresh per block. The book is guaranteed never crossed.
- Each message is a full refresh, not a diff. Replace your local book state wholesale on every update. There is nothing to merge, which also means a dropped message costs you one block, not book integrity.
- One coin per subscription. The request takes a single
coin. Multiplex by opening one stream per market; updates are one-per-block, so even dozens of subscriptions stay cheap. - Cadence is the block, not the order. You see the book as of each block, not every intra-block change. For order-level granularity use the L4 book or raw book diffs.
- Never crossed is a guarantee. Best bid is always strictly below best ask. You can drop the defensive checks other feeds force on you.
Sample message
{ "coin": "BTC", "block_height": 612408133, "time": "2026-06-09T08:14:02.418Z", "is_snapshot": false, "levels": [ [ { "px": "67011.0", "sz": "3.41280", "n": 14 }, { "px": "67010.0", "sz": "1.20019", "n": 6 } ], [ { "px": "67012.0", "sz": "2.88457", "n": 11 }, { "px": "67013.0", "sz": "5.10022", "n": 19 } ] ]}One update per block per subscribed coin gives predictable, low-bandwidth depth. Use n_levels: 1 as a top-of-book ticker.
Recipes
Top-of-book ticker
Subscribe with n_levels: 1 and you get best bid/ask once per block: a low-bandwidth price feed for any of the 462+ markets, with resting-order counts included.
grpcurl -H 'x-api-key: YOUR_KEY' \ -d '{"coin":"HYPE","n_levels":1}' \ stream.hyperliquidrpc.com:443 \ hyperliquid.OrderBookStreaming/StreamL2BookMid-price and spread
levels[0] is bids and levels[1] is asks, best first. Compute mid and spread with decimals. The px strings are byte-exact, so two consumers of the same block always agree.
from decimal import Decimal for msg in stub.StreamL2Book(request, metadata=meta): best_bid = Decimal(msg.levels[0][0].px) best_ask = Decimal(msg.levels[1][0].px) mid = (best_bid + best_ask) / 2 spread_bps = (best_ask - best_bid) / mid * 10_000Depth imbalance signal
Sum sz across the top N levels on each side and track the ratio. Because every message is a consistent post-block snapshot, the signal needs no sequencing logic at all.
from decimal import Decimal def depth(levels): return sum(Decimal(l.sz) for l in levels) for msg in stub.StreamL2Book(request, metadata=meta): bid_depth, ask_depth = depth(msg.levels[0]), depth(msg.levels[1]) imbalance = bid_depth / (bid_depth + ask_depth)