Hyperliquid Real-Time Order Book Subscription

May 11, 2026

An order book is the core market data structure of an exchange. It shows, in real time, the price and size distribution of open buy and sell orders. For quant bots, arbitrage systems, and market dashboards, getting the latest order book state with millisecond-level latency can be the difference between a usable strategy and a stale one. Source: Hyperliquid docs.

Hyperliquid provides a WebSocket API that lets subscribers receive L2 order book snapshots and incremental updates in real time. This guide covers how to subscribe, how the messages are structured, how to maintain a local order book, and what to consider for reconnects and production reliability.

If you do not want to build and maintain market data infrastructure yourself, OneKey Perps already provides real-time market views and a practical trading workflow out of the box.

Key comparison table

DimensionWebSocket SubscriptionREST API Polling
Data LatencyMillisecond-level pushPolling interval (usually 100ms–1s)
Server LoadLow (one handshake, continuous push)High (new request established for each poll)
Data IntegrityOrder can be guaranteed by sequence numbersMay miss changes between two polls
Implementation ComplexityMedium (requires handling disconnection and reconnection)Low (simple loop)
Applicable ScenariosHigh-frequency trading, real-time monitoringLow-frequency queries, initialization state

WebSocket vs. REST polling

Before getting into implementation details, it is worth understanding why WebSocket is the standard choice for real-time order book data.

REST polling can be useful for occasional reads, historical data, or low-frequency monitoring. But for a live order book, it is usually not enough. Polling creates unnecessary request overhead, can miss fast changes between requests, and often introduces too much latency for market-making, arbitrage, or risk monitoring.

For scenarios that require real-time order book updates, WebSocket is effectively the right tool. Refer to the official Hyperliquid documentation for the complete WebSocket API specification and any current limits.

Establishing a WebSocket connection

Endpoint

Hyperliquid’s WebSocket endpoint, as commonly documented, is:

wss://api.hyperliquid.xyz/ws

Always treat the official Hyperliquid documentation as the source of truth in case endpoints or message formats change.

Basic connection example

import asyncio
import json
import websockets

WS_URL = "wss://api.hyperliquid.xyz/ws"

async def connect_and_subscribe():
    async with websockets.connect(WS_URL) as ws:
        # Send subscription message
        subscribe_msg = {
            "method": "subscribe",
            "subscription": {
                "type": "l2Book",
                "coin": "BTC"
            }
        }

        await ws.send(json.dumps(subscribe_msg))

        # Continuously receive messages
        async for raw_msg in ws:
            msg = json.loads(raw_msg)
            handle_message(msg)

asyncio.run(connect_and_subscribe())

Subscription message format

Subscribe to the L2 order book

To subscribe to the BTC L2 order book:

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

Hyperliquid supports subscribing to multiple markets. Send one subscription message per market, and each market will receive its own stream of updates.

Unsubscribe

{
  "method": "unsubscribe",
  "subscription": {
    "type": "l2Book",
    "coin": "BTC"
  }
}

Message types: snapshots and deltas

Initial snapshot

After subscribing, the server first sends a full order book snapshot. This contains the currently visible price levels and their sizes:

{
  "channel": "l2Book",
  "data": {
    "coin": "BTC",
    "time": 1714500000000,
    "levels": [
      [
        [{"px": "64000.0", "sz": "0.5", "n": 3}],
        [{"px": "64010.0", "sz": "0.3", "n": 2}]
      ]
    ]
  }
}

Field meanings:

  • px: price
  • sz: total size at that price level; 0 means the level has been cleared
  • n: number of orders at that price level

In the levels array, bids are generally sorted from high to low, while asks are sorted from low to high.

Incremental updates

After the snapshot, the server sends updates for price levels that have changed:

{
  "channel": "l2Book",
  "data": {
    "coin": "BTC",
    "time": 1714500000100,
    "levels": [
      [
        [{"px": "63990.0", "sz": "0.0", "n": 0}],
        [{"px": "64010.0", "sz": "0.8", "n": 4}]
      ]
    ]
  }
}

If sz is "0", "0.0", or numerically zero, that price level has been fully filled or canceled and should be removed from your local order book.

Maintaining a local order book

The main engineering challenge is not just receiving WebSocket messages. It is keeping your local order book consistent with the exchange’s state.

from sortedcontainers import SortedDict

class LocalOrderBook:
    def __init__(self, coin):
        self.coin = coin
        self.bids = SortedDict(lambda x: -float(x))  # bids: high to low
        self.asks = SortedDict(lambda x: float(x))   # asks: low to high
        self.last_time = None

    def apply_snapshot(self, data):
        self.bids.clear()
        self.asks.clear()

        levels = data["levels"][0]

        for bid in levels[0]:
            if float(bid["sz"]) > 0:
                self.bids[bid["px"]] = float(bid["sz"])

        for ask in levels[1]:
            if float(ask["sz"]) > 0:
                self.asks[ask["px"]] = float(ask["sz"])

        self.last_time = data["time"]

    def apply_delta(self, data):
        levels = data["levels"][0]

        for bid in levels[0]:
            px, sz = bid["px"], float(bid["sz"])
            if sz == 0:
                self.bids.pop(px, None)
            else:
                self.bids[px] = sz

        for ask in levels[1]:
            px, sz = ask["px"], float(ask["sz"])
            if sz == 0:
                self.asks.pop(px, None)
            else:
                self.asks[px] = sz

        self.last_time = data["time"]

    @property
    def best_bid(self):
        return next(iter(self.bids.items()), None)

    @property
    def best_ask(self):
        return next(iter(self.asks.items()), None)

    @property
    def mid_price(self):
        bid = self.best_bid
        ask = self.best_ask
        if bid and ask:
            return (float(bid[0]) + float(ask[0])) / 2
        return None

    @property
    def spread(self):
        bid = self.best_bid
        ask = self.best_ask
        if bid and ask:
            return float(ask[0]) - float(bid[0])
        return None

Message handling logic

A simple message handler can apply the first valid message as the initial snapshot and later messages as deltas:

order_book = LocalOrderBook("BTC")
is_initialized = False

def handle_message(msg):
    global is_initialized

    if msg.get("channel") != "l2Book":
        return

    data = msg["data"]

    if not is_initialized:
        order_book.apply_snapshot(data)
        is_initialized = True
        print(
            f"Order book initialized: "
            f"{len(order_book.bids)} bid levels, "
            f"{len(order_book.asks)} ask levels"
        )
    else:
        order_book.apply_delta(data)

    print(
        f"Best bid: {order_book.best_bid}, "
        f"best ask: {order_book.best_ask}, "
        f"spread: {order_book.spread}"
    )

In a production system, you should also validate timestamps, detect stale connections, and resync if the local state looks suspicious.

Handling disconnects and reconnects

WebSocket connections can drop because of network instability, server-side disconnects, client-side timeouts, or infrastructure changes. A production system should reconnect automatically.

import asyncio
import json
import websockets

async def subscribe_with_reconnect(coin, handle_fn, max_retries=None):
    retries = 0

    while max_retries is None or retries < max_retries:
        try:
            async with websockets.connect(
                WS_URL,
                ping_interval=20,
                ping_timeout=10
            ) as ws:
                retries = 0  # reset after a successful connection

                await ws.send(json.dumps({
                    "method": "subscribe",
                    "subscription": {
                        "type": "l2Book",
                        "coin": coin
                    }
                }))

                async for raw_msg in ws:
                    handle_fn(json.loads(raw_msg))

        except (websockets.ConnectionClosed, ConnectionError, OSError) as e:
            retries += 1
            wait = min(2 ** retries, 60)  # exponential backoff, capped at 60 seconds
            print(f"Connection closed ({e}); retrying in {wait}s, attempt {retries}")

            # Important: after reconnecting, reset local initialization state
            # and rebuild the book from the next full snapshot.
            await asyncio.sleep(wait)

When reconnecting, reset your local initialization state. After a new subscription, the server should provide a fresh snapshot; you should not assume you can continue applying deltas from the previous connection.

Sequence, timestamps, and message ordering

Hyperliquid WebSocket messages include a timestamp field. You can use it to detect stale or out-of-order messages.

For production systems, consider these checks:

  • Track the timestamp of the last processed message.
  • If a new message is older than the last processed one, treat it as stale and discard it.
  • If no message arrives for longer than your expected threshold, send a ping or reconnect.
  • For latency-sensitive strategies, consider infrastructure location, network routing, and dedicated connectivity where appropriate.

These checks do not eliminate risk, but they help prevent your trading system from acting on obviously stale or inconsistent data.

Subscribing to multiple markets

A single WebSocket connection can subscribe to multiple markets. For most use cases, it is better to reuse one connection instead of opening many parallel connections from the same IP.

coins = ["BTC", "ETH", "SOL"]
order_books = {coin: LocalOrderBook(coin) for coin in coins}
initialized = {coin: False for coin in coins}

async def multi_subscribe():
    async with websockets.connect(WS_URL) as ws:
        for coin in coins:
            await ws.send(json.dumps({
                "method": "subscribe",
                "subscription": {
                    "type": "l2Book",
                    "coin": coin
                }
            }))

        async for raw_msg in ws:
            msg = json.loads(raw_msg)

            if msg.get("channel") != "l2Book":
                continue

            data = msg["data"]
            coin = data["coin"]

            if coin not in order_books:
                continue

            if not initialized[coin]:
                order_books[coin].apply_snapshot(data)
                initialized[coin] = True
            else:
                order_books[coin].apply_delta(data)

This pattern makes it easier to manage many order books while keeping subscription overhead reasonable.

Practical use cases

Real-time order book data is commonly used for:

  • Trading bots: dynamically adjusting limit order prices based on bid/ask levels and spread.
  • Arbitrage systems: monitoring price differences between Hyperliquid and venues such as dYdX or GMX.
  • Market dashboards: visualizing depth, spread history, and liquidity distribution.
  • Risk systems: triggering alerts when spreads widen sharply or visible liquidity drops.

The more automated your workflow is, the more important it becomes to handle dropped messages, stale data, unexpected spreads, and reconnect logic properly.

OneKey Perps: trade without building the data stack

Building a reliable WebSocket order book service takes real engineering effort. You need connection management, resync logic, monitoring, alerting, and a way to avoid acting on stale data.

If your goal is to trade perpetuals rather than build quant infrastructure, OneKey Perps gives you a simpler path: real-time market display, a practical trading interface, and secure signing through the OneKey wallet ecosystem.

For users who connect to decentralized applications through WalletConnect docs, OneKey supports the standard WalletConnect flow and can interact with Web3 apps such as Hyperliquid while keeping signing under your control.

You can try OneKey by downloading the OneKey app, connecting your wallet, and using OneKey Perps as a streamlined workflow for on-chain perpetual trading. Review the risks carefully before trading, especially when using leverage.

FAQ

Q1: Does Hyperliquid limit WebSocket connections?

Refer to the official Hyperliquid documentation for current connection and rate limits. In general, avoid opening excessive concurrent connections from one IP. If you need multiple markets, reuse a single WebSocket connection and subscribe to multiple symbols when possible.

Q2: If I miss one delta update, will my local order book become incorrect?

Yes. If an incremental update is lost because of a network issue or client failure, your local book can diverge from the actual exchange state.

A practical approach is to reconnect and resubscribe whenever you suspect inconsistency, such as an abnormal spread, sudden best-price jump, or long gap with no messages. A fresh subscription lets you rebuild from a new snapshot.

Q3: Can real-time order book data be used for backtesting?

Not directly. Backtesting requires historical data, not only a live stream. If you want to backtest order book strategies, you need to store real-time tick data with timestamps and build a historical dataset over time. For broader preliminary research, you can also combine this with Hyperliquid historical candle data where suitable.

Q4: How do I know whether my WebSocket connection is healthy?

Track practical health signals, including:

  • time since the last received message;
  • ping/pong round-trip latency;
  • reconnect frequency;
  • whether the best bid, best ask, and spread look reasonable;
  • whether timestamps are moving forward as expected.

If the connection looks unhealthy, trigger a controlled reconnect and rebuild from a fresh snapshot.

Q5: Does using a OneKey wallet add trading latency?

OneKey hardware wallet signing typically completes within hundreds of milliseconds, which is acceptable for most discretionary and lower-frequency trading workflows.

For ultra-high-frequency strategies, such as sending many orders per second, traders may separate responsibilities: use an API Agent or hot-wallet setup for high-frequency signing while keeping main account fund management protected by OneKey. This separation can help balance operational speed and account security, but it does not remove trading or smart contract risk.

Conclusion

Real-time order book subscriptions are a core data source for serious trading systems. Hyperliquid’s WebSocket API provides a clear way to receive L2 snapshots and incremental updates, and with the patterns above, developers can build a local order book for bots, arbitrage monitors, dashboards, or risk systems.

But if you mainly want to trade perpetuals without maintaining low-level data infrastructure, OneKey Perps is a practical alternative. It combines a smoother trading workflow with the security model of the OneKey wallet ecosystem, so you can focus more on trade planning and risk management.

Risk warning: Real-time order book data only reflects market conditions at a specific moment and should not be the sole basis for trading decisions. Automated strategies based on order book data can incur significant losses due to market volatility, technical failures, latency, or flawed strategy logic. Perpetual futures trading is high risk and can result in the loss of your entire principal. This article is for technical reference only and is not financial, investment, legal, or tax advice. Trade only after you fully understand the risks.

Secure Your Crypto Journey with OneKey

View details for Shop OneKeyShop OneKey

Shop OneKey

The world's most advanced hardware wallet.

View details for Download AppDownload App

Download App

Scam alerts. All coins supported.

View details for OneKey SifuOneKey Sifu

OneKey Sifu

Crypto Clarity—One Call Away.