The public Hyperliquid order book API returns 20 price levels per side. On 3 November 2026, those 20 bid levels on the BTC perp covered $20 of price and 143 orders. At the same moment the full order-level (L4) book held about 62,500 resting orders across roughly 21,500 price levels. If your strategy cares about queue position, who is quoting, or how fast orders are canceled, the aggregated view leaves most of that out.
This post explains what each level of Hyperliquid order book data contains and which API serves it: the public l2Book, Dwellir's order book WebSocket, the gRPC streams, and historical snapshots. The numbers come from runs against mainnet on 3 November 2026, and every code sample below was tested against the live endpoints.
Short answer: L2 is price levels (price, total size, order count). L4 is every resting order with its owner, order ID, size, and placement time. Hyperliquid's public API serves L2 only, capped at 20 levels per side. For L4 you need a node's order-level output, served live as Dwellir's
l4BookWebSocket channel or theStreamL4BookgRPC method. For history, you need an order-level snapshot plus the raw book diffs that follow it.
What are L1, L2, L3, and L4 order book data?
The levels describe how much detail you see of one order book. They are not Hyperliquid protocol layers.
| Level | What you see | What stays hidden | Hyperliquid source |
|---|---|---|---|
| L1 | Best bid and best ask, with size and order count | Everything behind the top of book | bbo channel |
| L2 | Each price level: px, total sz, order count n | Individual orders, owners, queue order | l2Book (Info endpoint and WebSocket) |
| L3 | Each order: price, size, side, order ID | Who placed it | Derived from L4 by dropping the owner |
| L4 | Each order plus owner address, placement timestamp, orderType, tif, cloid | Nothing about resting state | Node order statuses and raw book diffs |
L1 to L3 come from traditional market data, where "L3" means order-by-order data. On Hyperliquid every order is placed by an onchain address, so the order-level feed carries that address, and providers call it L4. If you need anonymous order-level data, maintain an L4 book and drop the user field.
What the public Hyperliquid order book API returns
Hyperliquid exposes the book in two places, both free.
The Info endpoint returns a single L2 snapshot. It is limited to 20 levels per side, and you can pass nSigFigs (2 to 5) to round prices into wider levels:
curl -s https://api.hyperliquid.xyz/info \
-H 'Content-Type: application/json' \
-d '{"type":"l2Book","coin":"BTC"}' \
| jq '{bids: (.levels[0]|length), asks: (.levels[1]|length), best_bid: .levels[0][0]}'{ "bids": 20, "asks": 20, "best_bid": { "px": "76675.0", "sz": "5.04476", "n": 20 } }With "nSigFigs": 3, the same 20 bid levels reached from 76,600 down to 74,700, but each level then merged hundreds of orders (591 at the best bid).
The WebSocket l2Book subscription pushes the same shape. Per the Hyperliquid WebSocket docs, it sends 20 levels (5 with fast: true) on a block at least 0.5 seconds after the previous push. In practice it is slower: a 60-second test on 3 November 2026 received 13 BTC updates, with a median gap of 5.4 seconds between them. Dwellir's Tokyo latency benchmark found the same pattern, about one public update every 5 seconds against about 14 a second from Dwellir.
The limits that matter for book data, from Hyperliquid's rate limit docs:
- An Info
l2Bookrequest costs weight 2 out of 1,200 per minute per IP, so polling tops out at 600 snapshots a minute across all markets. - WebSocket clients get 10 connections, 1,000 subscriptions, and 10 unique users across user-specific subscriptions per IP.
- Per-order data on the public API is
orderUpdates, which only covers one address per subscription. There is no market-wide per-order channel.
The public API is enough for a price chart, a spread monitor, or a dashboard. It does not give you full depth, block-by-block updates, or individual orders.
100 levels per side: five times the public depth
Dwellir's order book WebSocket takes the same l2Book subscription and adds nLevels, from 1 to 100. At 100 levels per side that is five times the public feed's 20. In the 28 October 2026 Tokyo benchmark, it delivered the L2 book with a median of 177 ms against 282 ms on the public API, measured on a dedicated node; shared endpoints were within 15 ms of that.
subscription = {
"method": "subscribe",
"subscription": {"type": "l2Book", "coin": "BTC", "nLevels": 100, "nSigFigs": 5},
}The walkthrough below compares the two feeds side by side. Runnable Python examples, from a first connection to reconnection handling and analytics, are in the order book examples repository and explained step by step in Hyperliquid order book in Python.
What L4 data shows that L2 can't
To measure what the aggregation hides, we streamed the BTC L4 book from Dwellir's order book server for five minutes on 3 November 2026. The opening snapshot held 41,292 resting bids and 21,260 resting asks. One address alone had 3,741 resting bids.
The update stream was busier than the book suggests:
| Event in 5 minutes (BTC perp) | Count | Share |
|---|---|---|
| All order status events | 905,176 | 100% |
badAloPxRejected (post-only order would have crossed) | 706,751 | 78.1% |
open (order rested on the book) | 76,156 | 8.4% |
canceled | 75,149 | 8.3% |
perpMarginRejected | 34,726 | 3.8% |
iocCancelRejected | 9,453 | 1.0% |
filled | 757 | 0.08% |
| Other statuses | 2,184 | 0.2% |
About 99 orders were canceled for every order that filled. Of the 73,262 orders that were both placed and removed inside the window, the median one lived 541 ms, and 60.8% were gone within a second. None of this is visible in L2, where a level's size and n change but you can't tell whether an order was added, canceled, or traded against.
Queue position
Hyperliquid matches orders in price-time priority. Your fill probability at a price depends on the size resting ahead of you, and only an order-level book shows that. In the script run shown below, L2 reported the best BTC bid as 0.04998 BTC across 2 orders. L4 showed that one address held 0.0492 BTC of it and a second held 0.00078 BTC, both as Alo (post-only) orders.
Queue order is not always placement order. Under Hyperliquid's ALO priority fee rules, the tail of each level (orders placed within the last 400 ms) is sorted by priority fee. The node marks these inserts in its raw book diffs with an insertBefore field that names the order the new one jumps ahead of. In a 30-second sample of BTC raw diffs, 445 of 5,218 new orders (8.5%) carried insertBefore.
The derived L4 views (the l4Book channel and the StreamL4Book diffs) give a new order's size but not its insertBefore position, and their snapshots list each level in placement-time order. That is accurate enough for depth, whale tracking, and fill monitoring. If you model queue position order by order, build your levels from StreamRawBookDiffs, which keeps insertBefore.
Cancels, fills, and spoofing-style patterns
Each L4 update pairs order_statuses (why an order changed: open, canceled, filled, or one of about 20 rejection and cancel reasons) with book_diffs (what changed: new, update for a partial fill, modified, or remove). Combined, they answer questions L2 can't:
- Was that size pulled or traded? A
removepaired withcanceledis a pulled quote. Paired withfilled, it traded. To see the price, counterparty, and hash of each fill, also subscribe to the trades stream and match on order ID. - Is a wall real? Large orders that rest near the touch and are canceled within milliseconds as price approaches are the pattern spoofing detectors look for. Lifetimes like the 541 ms median above are only measurable per order.
- Is someone refilling a level? Hyperliquid has no native iceberg order type, but an address that re-posts similar size at the same price after each fill behaves like one. You can only see that by following the address across
newandremoveevents. - Who is quoting? Grouping resting orders by
usershows which addresses provide depth, which the whale tracking guide builds on.
These are heuristics. The user field is a pseudonymous address, and one firm can quote from many.
Keep an L4 book in sync over WebSocket
Dwellir's order book server uses the same JSON subscription format as the public WebSocket. An l4Book subscription opens with a Snapshot of every resting order, then sends one Updates message per block. This script keeps the book current for 60 seconds, then prints the L2 and L4 views of the best bid and a breakdown of order status events:
import asyncio, json, os
from collections import Counter
import websockets
URL = f"wss://api-hyperliquid-mainnet-orderbook.n.dwellir.com/{os.environ['DWELLIR_API_KEY']}/ws"
COIN, SECONDS = "BTC", 60
def apply_updates(orders, upd, statuses):
# "open" statuses carry the full order (side, tif, cloid); book_diffs carry the size change.
opened = {}
for s in upd["order_statuses"]:
statuses[s["status"]] += 1
if s["status"] == "open":
opened[s["order"]["oid"]] = s["order"]
for d in upd["book_diffs"]:
diff, oid = d["raw_book_diff"], d["oid"]
if diff == "remove": # filled or canceled: see order_statuses
orders.pop(oid, None)
elif "new" in diff:
orders[oid] = {**opened.get(oid, {}), "oid": oid, "user": d["user"],
"limitPx": d["px"], "sz": diff["new"]["sz"]}
elif "update" in diff and oid in orders: # partial fill
orders[oid]["sz"] = diff["update"]["newSz"]
elif "modified" in diff and oid in orders:
orders[oid]["sz"] = diff["modified"]["sz"]
def best_bid_level(orders):
bids = [o for o in orders.values() if o.get("side") == "B"]
best = max(float(o["limitPx"]) for o in bids)
return best, [o for o in bids if float(o["limitPx"]) == best]
async def main():
orders, statuses = {}, Counter()
# A full BTC snapshot is about 20 MB of JSON, so raise the client's message limit.
async with websockets.connect(URL, max_size=64 * 1024 * 1024) as ws:
await ws.send(json.dumps({"method": "subscribe",
"subscription": {"type": "l4Book", "coin": COIN}}))
deadline = None
async for raw in ws:
msg = json.loads(raw)
if msg.get("channel") != "l4Book":
continue
if "Snapshot" in msg["data"]:
bids, asks = msg["data"]["Snapshot"]["levels"]
orders = {o["oid"]: o for o in bids + asks}
print(f"snapshot: {len(bids):,} bid orders, {len(asks):,} ask orders")
deadline = asyncio.get_running_loop().time() + SECONDS
continue
apply_updates(orders, msg["data"]["Updates"], statuses)
if deadline and asyncio.get_running_loop().time() > deadline:
break
px, level = best_bid_level(orders)
total = sum(float(o["sz"]) for o in level)
print(f"\nL2 view of the best bid: px={px} sz={total:.5f} n={len(level)}")
print("L4 view of the same level:")
for o in level:
print(f" oid={o['oid']} user={o['user'][:10]}… sz={o['sz']} tif={o.get('tif')}")
n = sum(statuses.values())
print(f"\n{n:,} order status events in {SECONDS}s:")
for status, count in statuses.most_common(5):
print(f" {status:<22}{count:>9,} {count / n:.1%}")
asyncio.run(main())Output from one run on 3 November 2026 (addresses shortened):
snapshot: 41,485 bid orders, 21,126 ask orders
L2 view of the best bid: px=76700.0 sz=0.04998 n=2
L4 view of the same level:
oid=593115292163 user=0xe90bf24e… sz=0.0492 tif=Alo
oid=593115316228 user=0xeb473272… sz=0.00078 tif=Alo
254,383 order status events in 60s:
badAloPxRejected 202,273 79.5%
open 18,624 7.3%
canceled 18,580 7.3%
perpMarginRejected 11,028 4.3%
iocCancelRejected 2,213 0.9%The BTC snapshot is about 20 MB of JSON, and update messages averaged about 97 KB, so the default 1 MiB message limit in Python's websockets closes the connection on the first frame.
One test run also dropped with a keepalive timeout, so production code needs reconnection that resubscribes and rebuilds from the new snapshot. The order book server guide and the step-by-step Python tutorial cover both. Field definitions and every status value are in the L4 book reference.
Streaming L2 and L4 over gRPC
The same books are available as typed Protocol Buffer streams on Dwellir's Hyperliquid gRPC API. The MarketStreaming service has four order book methods:
| Method | Opens with | Then sends | Use it for |
|---|---|---|---|
StreamL2Book | Full L2 book for 1 coin (default 20 levels, up to 100) | Replacement snapshots | Depth without client-side state |
StreamL2BookDiff | One full book per coin, 1 to 20 coins | Changed levels, chained by seq/prev_seq | Many L2 books at lower bandwidth |
StreamL4Book | Typed snapshot of every order for 1 coin | Native JSON {order_statuses, book_diffs} per block | Full order detail plus lifecycle reasons |
StreamL4OrderUpdates | Snapshot as typed NEW entries | Typed NEW, UPDATE, REMOVE with a sequence | Maintaining an order map across one or many coins |
Protobuf is smaller than JSON. The BTC L4 snapshot was 7.7 MB over StreamL4Book against about 20 MB over the WebSocket, and it arrived in under a second. StreamL2Book delivered 270 frames in 20 seconds, about 13.5 a second.
This Python client keeps a per-order map from StreamL4OrderUpdates and stops on a sequence gap, which signals lost frames. Generate v3_pb2.py and v3_pb2_grpc.py from the published v3.proto with python -m grpc_tools.protoc first:
import os, time, grpc
import v3_pb2 as pb, v3_pb2_grpc as rpc # generated from v3.proto with grpcio-tools
NEW, UPDATE, REMOVE = (pb.L4_ORDER_DIFF_TYPE_NEW, pb.L4_ORDER_DIFF_TYPE_UPDATE,
pb.L4_ORDER_DIFF_TYPE_REMOVE)
channel = grpc.secure_channel(
"api-hyperliquid-mainnet-grpc.n.dwellir.com:443",
grpc.ssl_channel_credentials(),
options=[("grpc.max_receive_message_length", 64 * 1024 * 1024)], # opening snapshot is large
)
stub = rpc.MarketStreamingStub(channel)
metadata = [("x-api-key", os.environ["DWELLIR_API_KEY"])]
orders, expected_seq, started = {}, 1, time.time()
for frame in stub.StreamL4OrderUpdates(pb.L4OrderUpdatesRequest(coins=["BTC"]), metadata=metadata):
if frame.sequence != expected_seq: # a gap means lost frames: reconnect and resnapshot
raise RuntimeError(f"sequence gap: expected {expected_seq}, got {frame.sequence}")
expected_seq += 1
if frame.snapshot:
orders.clear()
for d in frame.diffs:
if d.diff_type in (NEW, UPDATE):
orders[d.oid] = (d.side, d.px, d.sz, d.user)
elif d.diff_type == REMOVE:
orders.pop(d.oid, None)
if frame.snapshot or frame.sequence % 50 == 0:
print(f"block {frame.block_number} seq {frame.sequence}: {len(orders):,} resting BTC orders")
if time.time() - started > 15:
breakblock 1207307886 seq 1: 62,527 resting BTC orders
block 1207307942 seq 50: 62,466 resting BTC orders
block 1207308011 seq 100: 62,568 resting BTC ordersA typed REMOVE doesn't say whether the order filled or was canceled. Use StreamL4Book, whose diffs include order_statuses, when the reason matters.
Choose WebSocket if you already use the public API format or want to start without generating stubs. Choose gRPC for typed messages, smaller snapshots, multi-coin order maps, and access to the raw node feeds (StreamRawBookDiffs, StreamOrderStatuses, StreamFills) on the same connection.
Historical Hyperliquid order book snapshots
The live APIs only describe the book from the moment you connect. For backtests and post-trade analysis you need stored book state, and there are two sources.
Hyperliquid's own hyperliquid-archive S3 bucket has L2 book snapshots under market_data/[date]/[hour]/l2Book/[coin].lz4. Per the Hyperliquid historical data docs, it is uploaded about once a month, data may be missing, and the requester pays transfer costs. It is L2 only, so you get no order IDs, owners, or queue order.
Dwellir's archive stores order-level state and the node's raw book changes:
| Prefix | Contents | From | Cadence |
|---|---|---|---|
orderbook_snapshots | Every resting order in every market, as <height>.json.lz4 | 2026-04-14 | One per 10,000 blocks (about every 12 minutes) |
node_raw_book_diffs_by_block | Every order added, resized, or removed, per block | 2026-01-22 | Hourly files |
node_order_statuses_by_block | Status events (open, canceled, filled, rejected) | 2026-01-22 | Hourly files |
To rebuild the book at any block, load the nearest orderbook_snapshots object at or before it, then apply node_raw_book_diffs_by_block records in block order. Raw diffs alone can't do this because they don't contain the starting state. The raw diffs keep insertBefore, so a reconstruction can preserve queue order through ALO priority inserts.
The files are large. On 21 October 2026 the archive held 120 snapshot objects (about 18.7 MB each compressed, covering all markets) and 25.2 GB of compressed raw book diffs for that one day. Each archive file covers every market, so you filter to your coins after download.
The historical data docs list coverage, sizes, and file counts per prefix. For recent windows, the gRPC GetOrderbookSnapshot and StreamRawBookDiffs methods can be positioned by timestamp or block within the endpoint's retained history.
If you are choosing a dataset for a backtest rather than rebuilding books, the Hyperliquid backtesting data comparison covers candles, tick data, and 20 sources.
Which Hyperliquid order book source to use
| Source | Depth | Updates | Per-order | History | Price |
|---|---|---|---|---|---|
Public Info l2Book | 20 levels per side | Poll; weight 2 of 1,200/min per IP | No | No | Free |
Public WebSocket l2Book | 20 levels (5 with fast) | About 1 every 5 s measured | No | No | Free |
Dwellir order book WebSocket (l2Book, l4Book, bbo, trades) | Up to 100 L2 levels; full L4 book | 1 per block (about 13 a second) | Yes | No | $199/month + 1 credit per message |
Dwellir gRPC (StreamL2Book, StreamL4Book, raw diffs) | Up to 100 L2 levels; full L4 book; raw node diffs | 1 per block | Yes, including insertBefore in raw diffs | Recent replay on raw feeds | $299/month + 1 credit per message |
Hyperliquid S3 market_data | L2 snapshots | Hourly files, uploaded about monthly | No | Yes, no completeness guarantee | Free, requester pays transfer |
| Dwellir archive | Every order, every market | Snapshot every 10,000 blocks, diffs every block | Yes | From 2026-01-22 (diffs), 2026-04-14 (snapshots) | Quoted per dataset |
A Dedicated Node Cluster, from $4,000/month, includes both the WebSocket and gRPC add-ons with no per-message charges. Full prices are on the Hyperliquid pricing page.
Message volume decides the metered cost. One L4 subscription sends one message per block, about 12 to 14 a second in these runs, which is roughly 1 million messages per coin per day. The Hyperliquid orderbook API page has a volume calculator for your coin and depth mix.
For dashboards, alerts, and spread monitors, the public l2Book or bbo is enough. Market making and execution on a few coins usually need L2 at up to 100 levels, plus L4 on the coins where queue position matters, over WebSocket or gRPC. Microstructure research, queue modelling, and backtests need the gRPC raw diffs for live data and the archive for history.
Other providers sell overlapping products. QuickNode streams L2 and L4 books over gRPC and has a good walkthrough of the L1 to L4 levels. Hydromancer streams L4 over WebSocket. Latency differs by provider and transport. In the October 2026 Tokyo benchmark, Dwellir's WebSocket L2 book had a 177 ms median against 282 ms for the public API, Hydromancer was 8 to 27 ms behind Dwellir on WebSocket order books, and QuickNode was tied or up to 13 ms ahead on gRPC.
Frequently asked questions
What is the difference between L2 and L4 order book data on Hyperliquid?
L2 gives one row per price level: price, total size, and the number of orders. L4 lists every resting order separately, with its order ID, owner address, size, placement time, order type, and time in force. On 3 November 2026 the BTC perp L4 book held about 62,500 resting orders, while the public l2Book response showed 20 levels per side.
Does the Hyperliquid API provide L4 order book data?
No. The public Info endpoint and WebSocket return the aggregated l2Book (20 levels per side) and bbo. Per-order data on the public API is limited to orderUpdates for a single address. Market-wide L4 comes from a node's order-status and raw book-diff output, which Dwellir serves as the l4Book WebSocket channel and the StreamL4Book and StreamL4OrderUpdates gRPC methods.
How many levels does the Hyperliquid l2Book API return?
At most 20 levels per side, or 5 with the WebSocket fast option. nSigFigs (2 to 5) widens each level by rounding prices. Dwellir's l2Book channel accepts nLevels from 1 to 100, five times the public depth.
Is there an L3 order book on Hyperliquid?
No feed is called L3. Hyperliquid books are fully attributed, so per-order data arrives with owner addresses (L4). To get an anonymous L3 view, keep the per-order map from an L4 stream and drop the user field.
Can I get historical Hyperliquid order book data?
Hyperliquid's own S3 bucket has hourly L2 snapshot files, uploaded about once a month with no completeness guarantee. Dwellir's archive has order-level orderbook_snapshots from 2026-04-14, one every 10,000 blocks, plus node_raw_book_diffs_by_block from 2026-01-22. A snapshot plus the diffs that follow rebuilds the book at any block. See generated order book snapshots.
How do I find my queue position on Hyperliquid?
Orders match in price-time priority, so your position is the size resting ahead of you at your price. ALO priority fees can reorder the last 400 ms of each level, and raw book diffs mark those inserts with insertBefore. Build the level from raw diffs if you need exact queue order.
How much does Hyperliquid L4 data cost?
On Dwellir, the Order Book add-on (l2Book, l4Book, bbo, trades over WebSocket) is $199/month and the gRPC add-on is $299/month, each on an API plan, plus 1 API credit per streamed message. A Dedicated Node Cluster from $4,000/month includes both, unmetered. Archive datasets are quoted per dataset.
Next steps
Start with the order book server docs for endpoints and subscription formats, or the gRPC overview for the proto and raw feeds. For archive coverage or a quote on orderbook_snapshots and raw book diffs, email support@dwellir.com with the prefix and date range, or contact the Dwellir team. To enable the Order Book or gRPC add-on, create an account on the Dwellir dashboard.





