⚠️Blast API (blastapi.io) ends Oct 31. Migrate to Dwellir and skip Alchemy's expensive compute units.
Switch Today →
Skip to main content

Order Book WebSocket API

Watch the WebSocket Walkthrough

Get an overview of the Dwellir-hosted Hyperliquid order book server, including deployment architecture and how to stream ultra-low latency data into your trading stack.

Overview

Dwellir provides a high-performance WebSocket server delivering real-time order book data from Hyperliquid with ultra-low latency. This service offers Level 2 (L2), Level 4 (L4), and trades streaming directly from Hyperliquid nodes, significantly faster than public API endpoints.

Key Features

  • Ultra-low latency - Direct node connectivity eliminates public API overhead
  • Full order book depth - Access to L4 data with individual order visibility
  • Real-time streaming - WebSocket push model for immediate updates
  • Efficient compression - Optimized bandwidth usage for high-frequency data
  • Institutional grade - Professional infrastructure with monitoring and support

Hosted Service by Dwellir

For production use cases requiring minimum latency, Dwellir offers hosted WebSocket servers with:

  • Direct Hyperliquid node connectivity for fastest possible data access
  • Geographic distribution for optimal routing
  • Professional infrastructure and 24/7 monitoring
  • Significantly faster than public API endpoints (~10-50ms improvement)

📧 Contact: ben@dwellir.com for access to hosted instances

Quick Start

Code Examples Repository

📦 Complete, runnable code examples: github.com/dwellir-public/hyperliquid-orderbook-server-code-examples

The repository includes 6 progressive examples covering:

  • Basic WebSocket connections
  • L2 and L4 order books
  • Multiple subscriptions
  • Reconnection handling
  • Market data analysis

Clone it to get started quickly with your own trading applications.

#!/usr/bin/env python3
"""
Simple Hyperliquid WebSocket API Example using Dwellir
Subscribes to BTC trades and displays incoming messages
"""

import asyncio
import json
import websockets

async def main():
# Contact ben@dwellir.com for server details
ws_url = "wss://your-instance.dwellir.com/ws"

# Connect to WebSocket
print(f"Connecting to {ws_url}...")
websocket = await websockets.connect(ws_url)
print("Connected!")

# Subscribe to BTC trades
subscribe_message = {
"method": "subscribe",
"subscription": {
"type": "trades",
"coin": "BTC"
}
}
await websocket.send(json.dumps(subscribe_message))
print("Subscribed to BTC trades\n")

# Listen for messages
try:
async for message in websocket:
data = json.loads(message)
print(f"Received: {json.dumps(data, indent=2)}\n")

except KeyboardInterrupt:
print("\nStopping...")
finally:
await websocket.close()
print("Disconnected")

if __name__ == "__main__":
asyncio.run(main())

API Documentation

WebSocket Endpoint

wss://<your-instance>.dwellir.com/ws

The server exposes a single WebSocket endpoint for all data streaming. Clients send JSON messages to manage subscriptions and receive real-time market data updates.

Connection Management

  1. Connect to the WebSocket endpoint
  2. Subscribe to desired data streams
  3. Receive real-time updates
  4. Handle reconnections gracefully
Important: Implement WebSocket Reconnections

The orderbook server software may exit from time to time if the L2 or L4 orderbook falls out of sync. Your client implementation must include automatic reconnection logic with exponential backoff to maintain continuous data feeds during these restarts.

Message Protocol

Client → Server Messages

Subscribe to Data Stream

{
"method": "subscribe",
"subscription": {
// Subscription configuration (see types below)
}
}

Unsubscribe from Data Stream

{
"method": "unsubscribe",
"subscription": {
// Same subscription object used to subscribe
}
}

Server → Client Messages

Subscription Confirmation

{
"channel": "subscriptionResponse",
"data": {
"method": "subscribe",
"subscription": {
// Echoed subscription details
}
}
}

Error Messages

{
"channel": "error",
"data": "Descriptive error message"
}

Common errors:

  • "Invalid subscription: coin not found" - Unsupported trading pair
  • "Invalid subscription: n_levels too high" - Exceeds 100 level limit
  • "Already subscribed" - Duplicate subscription attempt
  • "Order book not ready" - Server still initializing

Subscription Types

1. Trades Stream

Real-time trade executions for a specific coin.

Subscribe

{
"type": "trades",
"coin": "BTC"
}

Response Stream

{
"channel": "trades",
"data": [
{
"coin": "BTC",
"side": "A", // "A" = Ask (sell), "B" = Bid (buy)
"px": "106296.0", // Execution price
"sz": "0.00017", // Trade size
"time": 1751430933565, // Unix timestamp (ms)
"hash": "0xde93a8a0...", // Transaction hash
"tid": 293353986402527, // Trade ID
"users": [
"0xcc0a3b6e...", // Buyer address
"0xc64cc00b..." // Seller address
]
}
]
}

2. L2 Order Book

Level 2 order book with price level aggregation.

Subscribe

{
"type": "l2Book",
"coin": "BTC",
"nSigFigs": 3, // Optional: 2-5, price aggregation
"nLevels": 50, // Optional: 1-100, default 20
"mantissa": 5 // Optional: 2 or 5, requires nSigFigs=5
}

Response Stream

{
"channel": "l2Book",
"data": {
"coin": "BTC",
"time": 1751427259657,
"levels": [
[ // Bid levels [price, size, order count]
{"px": "106217.0", "sz": "0.001", "n": 1},
{"px": "106215.0", "sz": "0.001", "n": 1},
{"px": "106213.0", "sz": "0.27739", "n": 1}
],
[ // Ask levels
{"px": "106233.0", "sz": "0.26739", "n": 3},
{"px": "106258.0", "sz": "0.001", "n": 1},
{"px": "106270.0", "sz": "0.49128", "n": 2}
]
]
}
}

3. L4 Order Book

Level 4 order book with individual order visibility - the most detailed market microstructure data available.

Subscribe

{
"type": "l4Book",
"coin": "BTC"
}

Initial Snapshot

{
"channel": "l4Book",
"data": {
"coin": "BTC",
"time": 1751427259657,
"height": 123456,
"levels": [
[ // Bid orders (individual)
{
"user": "0xcc0a3b6e...",
"coin": "BTC",
"side": "B",
"limitPx": "106200.0",
"sz": "0.5",
"oid": 12345678,
"timestamp": 1751427259657,
"triggerCondition": "",
"isTrigger": false,
"triggerPx": "",
"isPositionTpsl": false,
"reduceOnly": false,
"orderType": "limit",
"tif": "GTC",
"cloid": null
}
],
[ // Ask orders (individual)
// Individual ask order objects
]
]
}
}

Incremental Updates

{
"channel": "l4Book",
"data": {
"time": 1751427259657,
"height": 123457,
"orderStatuses": [
// Order status changes (fills, cancellations)
],
"bookDiffs": [
// Order book modifications (new, update, remove)
]
}
}

Implementation Examples

Complete Examples

All examples below are available in the code examples repository with full implementations and instructions.

Example 1: L2 Order Book with Spread Display

Display real-time order book data with bid/ask spreads:

#!/usr/bin/env python3
"""
L2 Order Book Example - Aggregated Price Levels
Shows bid/ask spreads with price aggregation
"""

import asyncio
import json
import websockets

def display_orderbook(data):
"""Display the order book in a simple format"""
if data.get("channel") != "l2Book":
return

coin = data["data"]["coin"]
levels = data["data"]["levels"]

print(f"\n{'='*50}")
print(f"📊 {coin} Order Book")
print(f"{'='*50}")

# Display asks (sellers) - reverse order
print("\n🔴 ASKS (Sellers)")
print(f"{'Price':<15} {'Size':<15} {'Orders':<10}")
print("-" * 50)
for ask in reversed(levels[1][:5]): # Top 5 asks
print(f"{ask['px']:<15} {ask['sz']:<15} {ask['n']:<10}")

# Calculate spread
best_bid = float(levels[0][0]['px']) if levels[0] else 0
best_ask = float(levels[1][0]['px']) if levels[1] else 0
spread = best_ask - best_bid
spread_pct = (spread / best_bid * 100) if best_bid > 0 else 0

print(f"\n{'─'*50}")
print(f"💰 Spread: ${spread:.2f} ({spread_pct:.3f}%)")
print(f"{'─'*50}\n")

# Display bids (buyers)
print("🟢 BIDS (Buyers)")
print(f"{'Price':<15} {'Size':<15} {'Orders':<10}")
print("-" * 50)
for bid in levels[0][:5]: # Top 5 bids
print(f"{bid['px']:<15} {bid['sz']:<15} {bid['n']:<10}")

async def main():
ws_url = "wss://your-instance.dwellir.com/ws"

websocket = await websockets.connect(ws_url)
print("Connected!\n")

# Subscribe to ETH L2 order book
subscribe_message = {
"method": "subscribe",
"subscription": {
"type": "l2Book",
"coin": "ETH",
"nLevels": 10,
"nSigFigs": 5
}
}
await websocket.send(json.dumps(subscribe_message))

try:
async for message in websocket:
data = json.loads(message)
display_orderbook(data)
except KeyboardInterrupt:
await websocket.close()

if __name__ == "__main__":
asyncio.run(main())

Example 2: Multiple Subscriptions

Handle multiple coins and data types simultaneously:

#!/usr/bin/env python3
"""
Multiple Subscriptions Example
Subscribe to different data types and coins simultaneously
"""

import asyncio
import json
import websockets
from datetime import datetime

def handle_trade(data):
"""Handle incoming trade data"""
for trade in data["data"]:
coin = trade.get("coin")
side_icon = "🟢" if trade["side"] == "B" else "🔴"
timestamp = datetime.fromtimestamp(trade["time"] / 1000)
print(f"{side_icon} {coin}: {trade['side']} {trade['sz']} @ ${trade['px']} | {timestamp.strftime('%H:%M:%S')}")

def handle_l2_book(data):
"""Handle incoming L2 order book data"""
coin = data["data"]["coin"]
levels = data["data"]["levels"]

if levels[0] and levels[1]:
bid = levels[0][0]['px']
ask = levels[1][0]['px']
spread = float(ask) - float(bid)
print(f"📊 {coin} Book: Bid ${bid} | Ask ${ask} | Spread ${spread:.2f}")

async def main():
ws_url = "wss://your-instance.dwellir.com/ws"
websocket = await websockets.connect(ws_url)

# Subscribe to BTC trades
await websocket.send(json.dumps({
"method": "subscribe",
"subscription": {"type": "trades", "coin": "BTC"}
}))

# Subscribe to ETH trades
await websocket.send(json.dumps({
"method": "subscribe",
"subscription": {"type": "trades", "coin": "ETH"}
}))

# Subscribe to SOL L2 order book
await websocket.send(json.dumps({
"method": "subscribe",
"subscription": {"type": "l2Book", "coin": "SOL", "nLevels": 5}
}))

try:
async for message in websocket:
data = json.loads(message)
channel = data.get("channel")

if channel == "trades":
handle_trade(data)
elif channel == "l2Book":
handle_l2_book(data)
except KeyboardInterrupt:
await websocket.close()

if __name__ == "__main__":
asyncio.run(main())

Example 3: Robust Client with Reconnection

Production-ready client with automatic reconnection:

#!/usr/bin/env python3
"""
Robust WebSocket Client with Reconnection
Handles disconnections and automatically reconnects
"""

import asyncio
import json
import websockets

class RobustWSClient:
"""WebSocket client with automatic reconnection"""

def __init__(self, ws_url):
self.ws_url = ws_url
self.websocket = None
self.subscriptions = []
self.is_running = False
self.reconnect_delay = 1
self.max_reconnect_delay = 60

def add_subscription(self, sub_type, coin, **kwargs):
"""Add a subscription to track"""
sub = {
"method": "subscribe",
"subscription": {
"type": sub_type,
"coin": coin,
**kwargs
}
}
self.subscriptions.append(sub)

async def connect(self):
"""Connect and subscribe to all tracked subscriptions"""
try:
self.websocket = await websockets.connect(self.ws_url)
print(f"✅ Connected to {self.ws_url}")

# Resubscribe to all subscriptions
for sub in self.subscriptions:
await self.websocket.send(json.dumps(sub))
coin = sub["subscription"]["coin"]
sub_type = sub["subscription"]["type"]
print(f"✅ Subscribed to {coin} {sub_type}")

# Reset reconnect delay
self.reconnect_delay = 1

except Exception as e:
print(f"❌ Connection failed: {e}")
raise

async def listen(self):
"""Listen for messages with automatic reconnection"""
self.is_running = True

while self.is_running:
try:
if not self.websocket:
await self.connect()

async for message in self.websocket:
data = json.loads(message)
self.handle_message(data)

except websockets.exceptions.ConnectionClosed:
print(f"⚠️ Connection closed. Reconnecting in {self.reconnect_delay}s...")
await asyncio.sleep(self.reconnect_delay)

# Exponential backoff
self.reconnect_delay = min(self.reconnect_delay * 2, self.max_reconnect_delay)
self.websocket = None

except Exception as e:
print(f"❌ Error: {e}")
await asyncio.sleep(self.reconnect_delay)
self.websocket = None

def handle_message(self, data):
"""Process incoming messages"""
channel = data.get("channel")

if channel == "trades":
for trade in data["data"]:
side_icon = "🟢" if trade["side"] == "B" else "🔴"
print(f"{side_icon} {trade['coin']}: {trade['sz']} @ ${trade['px']}")
elif channel == "l2Book":
coin = data["data"]["coin"]
levels = data["data"]["levels"]
if levels[0] and levels[1]:
bid = levels[0][0]['px']
ask = levels[1][0]['px']
spread = float(ask) - float(bid)
print(f"📊 {coin}: Bid ${bid} | Ask ${ask} | Spread ${spread:.2f}")

async def main():
ws_url = "wss://your-instance.dwellir.com/ws"

client = RobustWSClient(ws_url)
client.add_subscription("trades", "BTC")
client.add_subscription("l2Book", "ETH", nLevels=5)

print("🚀 Starting robust WebSocket client...\n")

try:
await client.listen()
except KeyboardInterrupt:
print("\nStopping...")
client.is_running = False

if __name__ == "__main__":
asyncio.run(main())

Example 4: Market Metrics and Analysis

Calculate real-time market statistics:

#!/usr/bin/env python3
"""
Market Metrics and Data Analysis
Calculate VWAP, spreads, volume, and more
"""

import asyncio
import json
import websockets
from collections import deque

class MarketAnalyzer:
"""Analyze market data and calculate metrics"""

def __init__(self, history_size=100):
self.trades = deque(maxlen=history_size)
self.spreads = deque(maxlen=history_size)

def add_trade(self, price, size, side):
"""Add trade and update metrics"""
self.trades.append({
'price': float(price),
'size': float(size),
'side': side
})

def get_vwap(self):
"""Calculate Volume-Weighted Average Price"""
if not self.trades:
return 0

total_value = sum(t['price'] * t['size'] for t in self.trades)
total_volume = sum(t['size'] for t in self.trades)

return total_value / total_volume if total_volume > 0 else 0

def get_buy_sell_ratio(self):
"""Calculate buy/sell volume ratio"""
buy_vol = sum(t['size'] for t in self.trades if t['side'] == 'B')
sell_vol = sum(t['size'] for t in self.trades if t['side'] == 'A')

return buy_vol / sell_vol if sell_vol > 0 else float('inf')

def display_stats(self, coin):
"""Display current market statistics"""
if not self.trades:
return

print(f"\n{'='*60}")
print(f"📈 {coin} Market Analytics")
print(f"{'='*60}")
print(f"💰 VWAP: ${self.get_vwap():.2f}")
print(f"📊 Trades: {len(self.trades)}")
print(f"⚖️ Buy/Sell Ratio: {self.get_buy_sell_ratio():.2f}")
print(f"{'='*60}\n")

async def main():
ws_url = "wss://your-instance.dwellir.com/ws"
websocket = await websockets.connect(ws_url)

# Subscribe to BTC trades
await websocket.send(json.dumps({
"method": "subscribe",
"subscription": {"type": "trades", "coin": "BTC"}
}))

analyzer = MarketAnalyzer()
trade_count = 0

try:
async for message in websocket:
data = json.loads(message)

if data.get("channel") == "trades":
for trade in data["data"]:
analyzer.add_trade(trade["px"], trade["sz"], trade["side"])
trade_count += 1

# Display stats every 10 trades
if trade_count % 10 == 0:
analyzer.display_stats("BTC")

except KeyboardInterrupt:
analyzer.display_stats("BTC") # Final stats
await websocket.close()

if __name__ == "__main__":
asyncio.run(main())

More Examples

The code examples repository includes additional examples:

  • Multi-coin tracker: Track order books and trades for multiple coins
  • Advanced order book analysis: Calculate order book imbalances and liquidity metrics
  • Data export: Save market data to CSV or database

Each example is fully documented with setup instructions and learning objectives.

Performance & Technical Specifications

Latency Characteristics

Based on real-world benchmarking of 2,662 matched trades:

MetricPublic APIDwellir WebSocketImprovement
Mean Latency367.60ms338.88ms28.72ms faster (8.5%)
Median Latency263.00ms212.00ms51.00ms faster (24.1%)
Min Latency90.00ms77.00ms13.00ms faster (16.9%)
Max Latency9,118.00ms1,977.00ms7,141.00ms faster (361.2%)

Performance Victory Rate

In head-to-head comparison:

  • WebSocket has lower latency: 75.5% (2,010 out of 2,662 trades)

The WebSocket server delivers data faster in 3 out of 4 cases, with significantly more consistent performance and protection against extreme latency spikes.

Throughput Capabilities

  • Message Rate: Handles thousands of updates per second
  • Concurrent Subscriptions: Multiple coins and data types per connection
  • Compression: Levels 0-9 configurable for bandwidth optimization
  • Connection Limit: Contact for dedicated capacity requirements

Data Guarantees

  • Ordering: Updates delivered in blockchain order
  • Consistency: Block-level atomicity for state changes
  • Reliability: Automatic reconnection with state recovery
  • Completeness: No dropped messages under normal conditions

Use Cases

High-Frequency Trading

  • Direct L4 book access for optimal order placement
  • Minimal latency for arbitrage opportunities
  • Full market depth visibility for advanced strategies

Market Making

  • Real-time spread monitoring and adjustment
  • Order book imbalance detection
  • Individual order tracking via L4 data
  • Inventory management with position tracking

Analytics & Research

  • Complete trade history streaming
  • Order flow analysis and toxicity detection
  • Market microstructure studies
  • Volume and liquidity profiling

Risk Management

  • Real-time position monitoring
  • Liquidity assessment for large orders
  • Market impact modeling
  • Volatility tracking and alerts

Best Practices

Connection Management

import asyncio
import websockets
import logging

class ResilientWebSocket:
def __init__(self, uri):
self.uri = uri
self.reconnect_delay = 5

async def connect_with_retry(self):
while True:
try:
async with websockets.connect(self.uri) as ws:
logging.info("Connected to WebSocket")
await self.handle_connection(ws)
except Exception as e:
logging.error(f"Connection error: {e}")
await asyncio.sleep(self.reconnect_delay)

async def handle_connection(self, ws):
# Your message handling logic here
pass

Rate Limiting & Throttling

  • Batch subscription requests when connecting
  • Implement exponential backoff for reconnections
  • Monitor message queue depth to detect backpressure

Data Processing

  • Use separate threads/processes for heavy computations
  • Implement ring buffers for high-frequency updates
  • Consider using binary protocols for internal distribution

Get Started with Dwellir

Hosted WebSocket Service

Dwellir offers production-ready WebSocket servers with:

Direct node connectivity - Fastest possible data access
Geographic distribution - Servers in key trading locations
99.99% uptime SLA - Enterprise reliability
Dedicated support - Direct access to engineering team
Custom configurations - Tailored to your requirements

Contact Us

Ready to integrate ultra-low latency Hyperliquid data into your trading systems?

📧 Email: ben@dwellir.com
🌐 Website: dwellir.com
📊 Dashboard: dashboard.dwellir.com

Get in touch for:

  • API credentials and server endpoints
  • Custom data requirements
  • Enterprise pricing
  • Technical integration support
  • Performance benchmarks for your use case

Code Examples & Resources

📦 Code Examples Repository - Complete, runnable examples in Python covering:

  • WebSocket basics and connection handling
  • L2 and L4 order book processing
  • Multiple subscriptions and data routing
  • Production-ready reconnection logic
  • Market data analysis and metrics

🔧 Hyperliquid Order Book Server - Official server implementation for self-hosting


Access institutional-grade Hyperliquid market data with Dwellir's ultra-low latency WebSocket infrastructure. Contact us → to get started.