Skip to main content

L4 Order Book (Level 4)

The most detailed market data available - individual order visibility with user wallet addresses, order IDs, timestamps, and full order parameters. L4 data enables queue position tracking, whale watching, and advanced market microstructure analysis.

Code Examples Repository

How to Subscribe#

Send a subscription message to the WebSocket endpoint:

{
"method": "subscribe",
"subscription": {
"type": "l4Book",
"coin": "BTC"
}
}

Subscription Parameters#

ParameterTypeRequiredDescription
typestringYesMust be "l4Book"
coinstringYesTrading pair symbol (e.g., "BTC", "ETH", "xyz:MSTR", "@150")
HIP3 Markets

For HIP3 (permissionless perpetuals) markets, use the full coin label format with the xyz: prefix. For example, use "xyz:MSTR" for the MicroStrategy perpetual, not just "MSTR". Standard perpetuals like BTC and ETH do not require a prefix.

Spot Markets

For spot markets, use the @{index} format where the index is the spot asset index. For example, use "@150" for a specific spot market.

Unlike L2, L4 subscriptions do not support nLevels or nSigFigs - you receive the complete order book with all individual orders.

Message Types#

L4 subscriptions produce two types of messages:

  1. Initial Snapshot - Complete order book state when you subscribe
  2. Incremental Updates - Changes as orders are placed, modified, or filled

Initial Snapshot Response#

When you first subscribe, you receive a complete snapshot of the order book wrapped in a Snapshot object:

{
"channel": "l4Book",
"data": {
"Snapshot": {
"coin": "BTC",
"height": 854890775,
"levels": [
[
{
"user": "0xf9109ada2f73c62e9889b45453065f0d99260a2d",
"coin": "BTC",
"side": "B",
"limitPx": "90057",
"sz": "0.33289",
"oid": 289682065711,
"timestamp": 1767878782721,
"triggerCondition": "N/A",
"isTrigger": false,
"triggerPx": "0.0",
"isPositionTpsl": false,
"reduceOnly": false,
"orderType": "Limit",
"tif": "Alo",
"cloid": "0x4c4617dbd8b94d358285c5c6d5a43df3"
}
],
[
{
"user": "0x13558be785661958932ceac35ba20de187275a42",
"coin": "BTC",
"side": "A",
"limitPx": "90058",
"sz": "0.37634",
"oid": 289682176026,
"timestamp": 1767878800615,
"triggerCondition": "N/A",
"isTrigger": false,
"triggerPx": "0.0",
"isPositionTpsl": false,
"reduceOnly": false,
"orderType": "Limit",
"tif": "Alo",
"cloid": "0x000000000814768000001999b6671c90"
}
]
]
}
}
}
Snapshot Size

A full BTC order book snapshot typically contains 20,000-40,000+ orders. The example above shows one order per side for brevity.

Incremental Update Response#

After the initial snapshot, you receive incremental updates wrapped in an Updates object containing order_statuses and book_diffs.

Example 1: New Order

This example shows a new order being added to the book:

{
"channel": "l4Book",
"data": {
"Updates": {
"time": 1767878802703,
"height": 854890776,
"order_statuses": [
{
"time": "2026-01-08T13:26:42.703377851",
"user": "0xbc927e87d072dfac3693846a83fa6922cc6c5f2a",
"status": "open",
"order": {
"user": null,
"coin": "BTC",
"side": "B",
"limitPx": "90056.0",
"sz": "0.00014",
"oid": 289682192129,
"timestamp": 1767878802703,
"triggerCondition": "N/A",
"isTrigger": false,
"triggerPx": "0.0",
"isPositionTpsl": false,
"reduceOnly": false,
"orderType": "Limit",
"tif": "Alo",
"cloid": "0xa097c34ee13a42a1afeed2a5ce96b413"
}
}
],
"book_diffs": [
{
"user": "0xbc927e87d072dfac3693846a83fa6922cc6c5f2a",
"oid": 289682192129,
"px": "90056.0",
"coin": "BTC",
"raw_book_diff": {
"new": {
"sz": "0.00014"
}
}
}
]
}
}
}

Example 2: Order Size Update (Partial Fill)

This example shows an existing order being partially filled, with the size decreasing from 108.65 to 107.5:

{
"channel": "l4Book",
"data": {
"Updates": {
"time": 1767878902834,
"height": 854890880,
"order_statuses": [],
"book_diffs": [
{
"user": "0x97991003fd631e2923f40cab2a4fdc35e60dc807",
"oid": 316542552323,
"px": "84.371",
"coin": "SOL",
"raw_book_diff": {
"update": {
"origSz": "108.65",
"newSz": "107.5"
}
}
}
]
}
}
}

In this update:

  • The order at price 84.371 was partially filled
  • Original size (origSz): 108.65 SOL
  • New remaining size (newSz): 107.5 SOL

Response Field Reference#

This section provides a detailed breakdown of all fields in L4 messages. For specific field values like order statuses, see Order Status Values.

All L4 messages follow this top-level structure:

{
channel: "l4Book",
data: Snapshot | Updates
}

Message Type 1: Snapshot (Initial State)#

Structure: { channel: "l4Book", data: { Snapshot: {...} } }

Received once when you first subscribe. Contains the complete order book state.

Snapshot Object#

FieldTypeDescription
coinstringTrading pair symbol (e.g., "BTC", "ETH", "xyz:MSTR", "@150")
heightnumberHyperliquid block height
levels[Order[], Order[]]Two-element array: [bids, asks]. Each element is an array of Order objects.

Order Object (in levels arrays)#

Each order in the levels[0] (bids) and levels[1] (asks) arrays contains:

FieldTypeDescription
userstringWallet address of order owner (e.g., "0xf9109ada...")
coinstringTrading pair (e.g., "BTC")
side"B" | "A""B" = Bid (buy), "A" = Ask (sell)
limitPxstringLimit price (e.g., "90057")
szstringRemaining order size (e.g., "0.33289")
oidnumberUnique order ID (e.g., 289682065711)
timestampnumberOrder placement time in Unix milliseconds (e.g., 1767878782721)
triggerConditionstringTrigger condition type, or "N/A" if not a trigger order
isTriggerbooleanWhether this is a trigger/stop order
triggerPxstringTrigger price, or "0.0" if not a trigger order
isPositionTpslbooleanWhether this is a position take-profit/stop-loss order
reduceOnlybooleanWhether order can only reduce position
orderTypestringOrder type: "Limit", "Market", "Stop Market", "Stop Limit", "Scale", "TWAP"
tif"Gtc" | "Ioc" | "Alo"Time-in-force: "Gtc" (Good til Cancel), "Ioc" (Immediate or Cancel), "Alo" (Add Liquidity Only)
cloidstringClient order ID - hex string provided by user (e.g., "0x4c4617dbd8b94d35...")

Message Type 2: Updates (Incremental Changes)#

Structure: { channel: "l4Book", data: { Updates: {...} } }

Received continuously after the snapshot. Contains incremental changes to the order book.

Updates Object#

FieldTypeDescription
timenumberUnix timestamp in milliseconds (e.g., 1767878802703)
heightnumberHyperliquid block height - increments with each update
order_statusesOrderStatus[]Array of order status changes (new orders, fills, cancellations, rejections). See Order Status Values for status meanings.
book_diffsBookDiff[]Array of order book modifications (additions, removals, size changes)
tradesTrade[]Array of trade executions (often empty when no trades occurred)
Trades Array

The trades array is included when orders match and execute. Each trade contains execution details including price, size, maker/taker order IDs, timestamp, and direction. Many L4 update messages will have an empty trades array ([]) when no executions occurred. Trades appear during active trading periods when orders match.

OrderStatus Object (in order_statuses array)#

FieldTypeDescription
timestringISO-8601 timestamp with nanosecond precision (e.g., "2026-01-08T13:26:42.703377851")
userstringWallet address of order owner
statusstringOrder status: "open", "filled", "canceled", "badAloPxRejected", etc. See Order Status Values for all possible values and their meanings.
orderOrderOrder object with same structure as Order Object above (but user field may be null)

BookDiff Object (in book_diffs array)#

The book_diffs array contains changes to individual orders in the book. Each diff describes what happened to a specific order - whether it was added (new), partially filled (update), modified (modified), or removed (remove).

Common fields present in all diff types:

FieldTypeDescription
userstringWallet address of order owner (e.g., "0xbc927e87...")
oidnumberUnique order ID being modified (e.g., 289682192129)
pxstringPrice level where order exists/existed (e.g., "90056.0")
coinstringTrading pair (e.g., "BTC")
raw_book_diffNewDiff | UpdateDiff | ModifiedDiff | "remove"Describes what changed. Can be: { new: { sz } } for new orders, { update: { origSz, newSz } } for partial fills, { modified: { sz } } for amendments, or "remove" for cancellations/complete fills. See BookDiff Types below for detailed specifications.

BookDiff Types (the raw_book_diff field)#

The raw_book_diff field indicates what happened to an order. It can take four forms:

TypeTypeScript DefinitionDescription
NewDiff{ new: { sz: string } }New order added to the book. The sz field contains the order size. Reference the corresponding entry in order_statuses with matching oid to get full order details (side, tif, etc.).
RemoveDiff"remove"Order completely removed from book. Can occur due to: full fill (check order_statuses for filled status), user cancellation (canceled), system cancellation, or order expiration.
UpdateDiff{ update: { origSz: string, newSz: string } }Order size changed (usually decreased due to partial fill). Contains both the original size (origSz) and the new remaining size (newSz).
ModifiedDiff{ modified: { sz: string } }Order size modified (typically from order amendments). The sz field contains the new remaining size. Unlike UpdateDiff, this only provides the new size without the original.

Order Status Values#

The status field in order_statuses indicates the result of order processing. Understanding these statuses is critical for debugging order placement issues.

StatusTypeDescriptionDebugging Notes
openSuccessOrder successfully placed and resting on the bookMost common successful status. Order is live and can be filled.
filledSuccessOrder fully executedOrder matched completely. Check book_diffs for removal.
triggeredSuccessTrigger/stop order activatedConditional order has been triggered and converted to regular order.
canceledCancellationOrder canceled by user or systemStandard cancellation. Check if user-initiated or system-triggered.
reduceOnlyCanceledCancellationReduce-only order was canceledPosition closed or order would have increased position instead of reducing.
selfTradeCanceledCancellationOrder canceled to prevent self-tradingSame user's buy and sell orders would have matched. Exchange prevented self-execution.
marginCanceledCancellationOrder canceled due to insufficient marginUser's margin balance insufficient to maintain the order.
openInterestCapCanceledCancellationOrder canceled due to open interest cap reachedMarket has reached maximum open interest limit. Try again later or use different market.
scheduledCancelCancellationOrder canceled on a scheduled basisOrder was automatically canceled based on time or condition schedule.
siblingFilledCanceledCancellationOrder canceled because sibling order filledPaired/bracket order canceled when the primary order executed. Common with OCO (One-Cancels-Other) orders.
badAloPxRejectedRejectionAdd-liquidity-only order rejected (never reached book)Most common rejection (~70% in production data). ALO order would have crossed spread and executed as taker. Price was too aggressive for maker-only order.
iocCancelRejectedRejectionImmediate-or-cancel order rejected (never reached book)IOC order couldn't fill immediately at specified price. No matching liquidity available.
perpMarginRejectedRejectionPerpetual futures order rejected (never reached book)Insufficient margin to open the position. Check account balance and leverage.
perpMaxPositionRejectedRejectionPerpetual order rejected - exceeds max position size (never reached book)Order would exceed maximum allowed position size for this market. Reduce order size or close existing positions.
minTradeNtlRejectedRejectionMinimum notional value rejected (never reached book)Order size (price Ɨ quantity) below exchange minimum. Increase order size.
reduceOnlyRejectedRejectionReduce-only order rejected (never reached book)Order marked reduce-only but would have increased position or no position exists to reduce.
insufficientSpotBalanceRejectedRejectionInsufficient spot token balance (never reached book)Not enough spot token balance to place order. Deposit more tokens or reduce order size.
oracleRejectedRejectionOrder rejected due to oracle price issues (never reached book)Oracle price feed unavailable or stale. Wait for oracle to update or check market status.
positionFlipAtOpenInterestCapRejectedRejectionPosition flip rejected at open interest cap (never reached book)Order would flip position direction when market is at OI cap. Close existing position first.
positionIncreaseAtOpenInterestCapRejectedRejectionPosition increase rejected at open interest cap (never reached book)Cannot increase position size when market has reached open interest limit.
tooAggressiveAtOpenInterestCapRejectedRejectionOrder too aggressive at open interest cap (never reached book)Order price too aggressive when market near OI cap. Use less aggressive limit price.

Code Examples#

#!/usr/bin/env python3

import asyncio
import json
import os
import websockets
from dotenv import load_dotenv
from pathlib import Path

# Load environment variables
env_path = Path(__file__).parent.parent / '.env'
load_dotenv(env_path)


async def main():
ws_url = os.getenv("WEBSOCKET_URL", "wss://api-hyperliquid-mainnet-orderbook.n.dwellir.com/API_KEY/ws")

print(f"Connecting to: {ws_url}\n")

async with websockets.connect(ws_url, max_size=50 * 1024 * 1024) as websocket:
# Subscribe to BTC L4 orderbook
subscribe = {
"method": "subscribe",
"subscription": {
"type": "l4Book",
"coin": "BTC"
}
}

await websocket.send(json.dumps(subscribe))
print("āœ… Subscribed to BTC L4 orderbook\n")
print("=" * 60)

message_count = 0

# Listen for messages
async for message in websocket:
message_count += 1
data = json.loads(message)

# Determine message type
if 'data' in data:
if 'Snapshot' in data['data']:
snapshot = data['data']['Snapshot']
height = snapshot.get('height', 'N/A')
bids = len(snapshot.get('bids', []))
asks = len(snapshot.get('asks', []))
print(f"šŸ“ø Snapshot #{message_count} | Height: {height} | Bids: {bids} | Asks: {asks}")

elif 'Updates' in data['data']:
updates = data['data']['Updates']
height = updates.get('height', 'N/A')
num_updates = len(updates.get('order_statuses', []))
print(f"šŸ”„ Update #{message_count} | Height: {height} | Orders: {num_updates}")
else:
print(f"ā„¹ļø Message #{message_count}: {data.get('channel', 'unknown')}")

print("-" * 60)


if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\n\nāœ‹ Stopped by user")

Use Cases#

Queue Position Optimization#

Understand your place in the order queue:

  • Time priority: See exactly where your order sits at a price level
  • Fill probability: Estimate likelihood of execution based on orders ahead
  • Repositioning: Decide when to cancel and replace for better position

Whale Wallet Tracking#

Monitor large or notable traders:

  • Address tracking: Follow specific wallet addresses
  • Size alerts: Trigger on orders above threshold
  • Pattern detection: Identify accumulation or distribution

Market Microstructure Research#

Analyze order flow dynamics:

  • Order arrival rates: Study how orders enter the book
  • Cancellation patterns: Track order lifetime and modification frequency
  • Toxicity analysis: Measure adverse selection from order flow

Smart Order Routing#

Optimize order execution strategy:

  • Liquidity mapping: Know exactly what size exists at each level
  • Hidden liquidity: Detect when large orders are being worked
  • Impact estimation: Model expected slippage from current book state

Bandwidth Considerations#

L4 data is significantly higher bandwidth than L2:

AspectL2L4
Data per level3 fields (px, sz, n)15+ fields per order
Orders visibleAggregated count onlyEvery individual order
Update frequencyPer price levelPer order change
Typical message size1-5 KB10-100+ KB

Consider subscribing to L4 only for coins where you need individual order visibility.

Get Access#

Ready to integrate real-time Hyperliquid L4 order book data?


Stream institutional-grade Hyperliquid order book data with Dwellir's ultra-low latency infrastructure.