Hyperliquid WebSocket API: Low-Latency Setup for Trading Bots
Complete guide to connecting to Hyperliquid's WebSocket feeds for order book, trades, and account updates, with latency benchmarks, reconnection logic, and Python/Rust code patterns for production-grade bots.
Hyperliquid's WebSocket API is the only sensible data path for bots that need to react to order book changes in under 10 ms. The REST endpoint is fine for sending orders, but polling it for market data is a non-starter — you will miss fills, lag on liquidations, and burn rate-limit budget on useless round trips. Here is everything you need to run a production WebSocket connection against Hyperliquid without guessing.
The Endpoint and Subscription Model
The mainnet WebSocket endpoint is wss://api.hyperliquid.xyz/ws. Testnet lives at wss://api.hyperliquid-testnet.xyz/ws. Both use the same protocol — a JSON-framed message bus where you subscribe to channels and the server pushes updates as events occur.
Subscriptions are sent as JSON objects with a method of "subscribe" and a subscription object that names the channel and (where applicable) the coin:
{"method": "subscribe", "subscription": {"type": "l2Book", "coin": "ETH"}}
{"method": "subscribe", "subscription": {"type": "trades", "coin": "BTC"}}
{"method": "subscribe", "subscription": {"type": "orderUpdates", "user": "0xYOUR_ADDRESS"}}
{"method": "subscribe", "subscription": {"type": "userFills", "user": "0xYOUR_ADDRESS"}}
The four channels you will almost always need:
l2Book— full level-2 snapshot on subscribe, then incremental diffs. Bids and asks arrive as[price, size]pairs; a size of"0"means the level was removed.trades— public tape, one message per aggressor fill. Latency from execution to delivery is typically 1–3 ms measured at a co-located server.orderUpdates— per-user order lifecycle events: open, filled, cancelled, rejected. This is what you use to close the loop between your order send and its acknowledgement.userFills— redundant withorderUpdatesfor fill data but easier to parse for P&L accounting; keep both subscribed in production.
Maintaining the Order Book in Memory
Do not trust snapshot-then-diff naively. The server sends a snapshot the moment you subscribe, but diffs can arrive in the same WebSocket frame before your snapshot handler finishes. The safe pattern:
- Buffer all incoming messages into a queue the moment the connection opens.
- Send the
l2Booksubscription. - When a message with
"type": "l2Book"and"isSnapshot": truearrives, apply it as the base state and replay the buffered diffs that have atimenewer than the snapshottime. - Apply subsequent diffs in arrival order, merging by price level.
In Python, a collections.OrderedDict keyed by price (as Decimal — never float for price levels) with bid/ask sides separated is sufficient for anything under ~500 active levels. In Rust, a BTreeMap<Decimal, Decimal> gives you O(log n) inserts and the sorted order you need for best-bid/best-ask in one call to iter().next_back() / iter().next().
A common mistake: rebuilding the entire book from scratch on every diff because the code skipped the snapshot step. This appears to work during quiet markets and produces wildly wrong quotes during volatility spikes when diff messages arrive faster than the "snapshot" replacement loop.
Latency Profile and What Moves It
From a VPS in the same AWS us-east-1 region as Hyperliquid's matching engine, round-trip WebSocket latency (subscribe → first message) sits at 4–8 ms under normal load. During high-volatility periods with many simultaneous subscribers, tail latency can spike to 25–40 ms — this is almost always message queuing on Hyperliquid's side, not network.
For market-making bots the relevant number is not connection latency but diff-to-quote latency: the wall-clock time between receiving a price level change and having a new order in flight. In Python with asyncio and websockets, best-case is around 0.8–1.5 ms of application-side processing per book update. In Rust with tokio and tungstenite, you can get that under 0.1 ms consistently. If your strategy alpha decays in less than 5 ms, you are writing Rust. If it decays in 50 ms or more, Python is fine and the simpler debugging will pay off in uptime.
One non-obvious latency source: JSON deserialization. A full l2Book snapshot for a liquid market can be 200–800 KB of JSON. Use ujson or orjson in Python instead of the stdlib json module — the difference is 3–8x on large payloads. In Rust, simd-json is worth the dependency for snapshot parsing.
Reconnection Logic That Doesn't Lose State
The WebSocket connection will drop. Infrastructure maintenance, brief network hiccups, and Hyperliquid server restarts all happen. Every production bot needs a reconnection loop that:
- Detects liveness actively, not just on close events. Send a WebSocket ping every 20 seconds and treat a missing pong within 5 seconds as a dead connection. The server sends its own pings; respond to those too.
- Backs off exponentially on repeated failures — start at 100 ms, cap at 30 s, reset on first successful message.
- Rebuilds book state from scratch on reconnect, not from the pre-disconnect snapshot. Treat reconnect exactly like a new connection.
- Suppresses order sends during the reconnect window. A bot that keeps quoting against a stale book is worse than one that pauses.
import asyncio, websockets, json
ENDPOINT = "wss://api.hyperliquid.xyz/ws"
COINS = ["ETH", "BTC"]
async def run():
backoff = 0.1
while True:
try:
async with websockets.connect(
ENDPOINT,
ping_interval=20,
ping_timeout=5,
max_size=10 * 1024 * 1024, # 10 MB for large snapshots
) as ws:
for coin in COINS:
await ws.send(json.dumps({
"method": "subscribe",
"subscription": {"type": "l2Book", "coin": coin}
}))
backoff = 0.1 # reset on successful connect
async for raw in ws:
handle(json.loads(raw))
except Exception as e:
await asyncio.sleep(backoff)
backoff = min(backoff * 2, 30)
asyncio.run(run())
The max_size parameter is critical. The default in most WebSocket libraries is 1 MB; a BTC snapshot during a volatile period will exceed that and the library will close the connection silently.
Account Update Channels and Authentication
The orderUpdates and userFills channels are not authenticated at the WebSocket level — you simply pass your wallet address. Hyperliquid filters server-side by address. This means anyone who knows your address can subscribe to your order feed, which is fine for a trading bot (your address is public on-chain) but relevant if you are building something where fill timing is sensitive alpha.
For bots built around execution strategies like TWAP, you will want to cross-reference orderUpdates fill events against your internal order state immediately on receipt, updating remaining quantity and re-scheduling the next slice before the next event loop tick. Doing this asynchronously after a queue hop adds 0.3–1 ms of unnecessary latency.
Running Multiple Coins on One Connection
The same WebSocket connection handles subscriptions for multiple coins. There is no documented hard limit on the number of subscriptions, but in practice more than ~40 concurrent l2Book subscriptions on a single connection starts to produce visible message batching delays on Hyperliquid's side. For broader market-making or arbitrage strategies across many instruments, split subscriptions across two or three connections and merge the book state in your main event loop.
The dispatch pattern that holds up best under load: one WebSocket coroutine per connection that does nothing but recv() and puts raw bytes onto an asyncio.Queue, and a separate set of consumer coroutines that parse and act. Keep the recv loop as thin as possible — deserialization, book updates, and order sends all happen downstream.
If you are building a production bot on Hyperliquid and need the WebSocket integration, risk controls, and execution logic handled correctly from day one, get in touch — we have shipped these systems and know where the edges are.
Need a bot like this built?
We design, build and run trading bots on Solana, Hyperliquid and Polymarket.
Start a projectMore from the blog
Public vs Paid RPC: How to Run a Fair Benchmark
A fair comparison of public and paid RPC endpoints must account for quotas, workload, freshness and support guarantees.
Read articleRPC Benchmark Percentiles Explained: p50, p95 and p99
Why averages hide the latency spikes that break trading bots, wallets and production blockchain applications.
Read articleHyperliquid REST vs WebSocket Benchmark: What to Measure
A benchmark plan for Hyperliquid market data that separates request latency from streaming freshness and recovery.
Read article