Skip to content
New guideAgent Cookbook 12 copy-ready prompts for AI coding agents.See recipes
All blog posts
Hyperliquid Order Book Data: L1 to L4 Explained, API Options, and Historical Snapshots

Hyperliquid Order Book Data: L1 to L4 Explained, API Options, and Historical Snapshots

By Ben Chatwin18min read

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 l4Book WebSocket channel or the StreamL4Book gRPC 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.

LevelWhat you seeWhat stays hiddenHyperliquid source
L1Best bid and best ask, with size and order countEverything behind the top of bookbbo channel
L2Each price level: px, total sz, order count nIndividual orders, owners, queue orderl2Book (Info endpoint and WebSocket)
L3Each order: price, size, side, order IDWho placed itDerived from L4 by dropping the owner
L4Each order plus owner address, placement timestamp, orderType, tif, cloidNothing about resting stateNode 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:

Bash
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]}'
JSON
{ "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 l2Book request 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.

Python
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)CountShare
All order status events905,176100%
badAloPxRejected (post-only order would have crossed)706,75178.1%
open (order rested on the book)76,1568.4%
canceled75,1498.3%
perpMarginRejected34,7263.8%
iocCancelRejected9,4531.0%
filled7570.08%
Other statuses2,1840.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 remove paired with canceled is a pulled quote. Paired with filled, 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 new and remove events.
  • Who is quoting? Grouping resting orders by user shows 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:

Python
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):

Text
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:

MethodOpens withThen sendsUse it for
StreamL2BookFull L2 book for 1 coin (default 20 levels, up to 100)Replacement snapshotsDepth without client-side state
StreamL2BookDiffOne full book per coin, 1 to 20 coinsChanged levels, chained by seq/prev_seqMany L2 books at lower bandwidth
StreamL4BookTyped snapshot of every order for 1 coinNative JSON {order_statuses, book_diffs} per blockFull order detail plus lifecycle reasons
StreamL4OrderUpdatesSnapshot as typed NEW entriesTyped NEW, UPDATE, REMOVE with a sequenceMaintaining 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:

Python
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:
        break
Text
block 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 orders

A 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:

PrefixContentsFromCadence
orderbook_snapshotsEvery resting order in every market, as <height>.json.lz42026-04-14One per 10,000 blocks (about every 12 minutes)
node_raw_book_diffs_by_blockEvery order added, resized, or removed, per block2026-01-22Hourly files
node_order_statuses_by_blockStatus events (open, canceled, filled, rejected)2026-01-22Hourly 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

SourceDepthUpdatesPer-orderHistoryPrice
Public Info l2Book20 levels per sidePoll; weight 2 of 1,200/min per IPNoNoFree
Public WebSocket l2Book20 levels (5 with fast)About 1 every 5 s measuredNoNoFree
Dwellir order book WebSocket (l2Book, l4Book, bbo, trades)Up to 100 L2 levels; full L4 book1 per block (about 13 a second)YesNo$199/month + 1 credit per message
Dwellir gRPC (StreamL2Book, StreamL4Book, raw diffs)Up to 100 L2 levels; full L4 book; raw node diffs1 per blockYes, including insertBefore in raw diffsRecent replay on raw feeds$299/month + 1 credit per message
Hyperliquid S3 market_dataL2 snapshotsHourly files, uploaded about monthlyNoYes, no completeness guaranteeFree, requester pays transfer
Dwellir archiveEvery order, every marketSnapshot every 10,000 blocks, diffs every blockYesFrom 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.

Start free.1 response = 1 credit.

100,000 responses a day at 20 per second, on 150+ networks. Upgrade when you need more.