Hyperfolio
Guide

Hyperliquid WebSocket API 2026: Subscriptions & Heartbeats

Hyperliquid WebSocket API guide: subscribe format, trades, l2Book, bbo and userFills feeds, snapshots, 60-second heartbeat, post requests and a Python example.

October 20, 20269 min

Hyperliquid WebSocket API Guide 2026: Subscriptions, Heartbeats and Post Requests

The short version: connect to wss://api.hyperliquid.xyz/ws, send {"method":"subscribe","subscription":{...}} for each feed, and read messages by their channel field. Send {"method":"ping"} if a feed can go quiet, because the server closes any connection it has not sent a message to in 60 seconds. User feeds such as userFills start with a snapshot tagged isSnapshot: true. You can also send info requests and signed actions over the same socket with {"method":"post"}.

REST polling works for a dashboard that refreshes every minute. If you want live order books, trade prints or a fill stream for a wallet, the Hyperliquid WebSocket API is the right tool: one connection, many feeds, and no per-request weight. This guide covers the message formats, the feeds people use most, what we measured on mainnet on 20 October 2026, and a Python script you can run as-is. If you only need to see a wallet's PnL and positions, you do not need any of this: Hyperfolio already does it read-only.

Endpoints and message format

Everything is JSON text frames. There are four client methods:

MethodExampleServer reply
subscribe{"method":"subscribe","subscription":{"type":"trades","coin":"BTC"}}channel: "subscriptionResponse", then data on channel: "trades"
unsubscribeSame object as the original subscriptionFeed stops
ping{"method":"ping"}{"channel":"pong"}
post{"method":"post","id":1,"request":{"type":"info","payload":{...}}}channel: "post" with the same id

The acknowledgement echoes your subscription with defaults filled in. When we subscribed to l2Book for BTC, the ack came back as {"type":"l2Book","coin":"BTC","nSigFigs":null,"mantissa":null,"fast":false}. Use the ack to confirm the server understood exactly what you asked for.

The subscriptions worth knowing

The official docs list more than 20 feed types. These are the ones that cover most trading, analytics and tracking work:

FeedSubscriptionWhat you get
allMids{"type":"allMids"} (optional dex)Mid prices for every market. Spot mids only come with the first perp dex.
trades{"type":"trades","coin":"BTC"}Arrays of prints with price, size, side, time, tid and users: [buyer, seller]
l2Book{"type":"l2Book","coin":"BTC"} (optional nSigFigs, mantissa, fast)Order book snapshots: 20 levels per side, or 5 with fast: true
bbo{"type":"bbo","coin":"BTC"}Best bid and offer, sent only when it changes on a block
candle{"type":"candle","coin":"BTC","interval":"1m"}The live candle. Intervals from 1m to 1M.
activeAssetCtx{"type":"activeAssetCtx","coin":"BTC"}Funding, open interest, mark, oracle and mid price, 24h volume
userFills{"type":"userFills","user":"0x…"} (optional aggregateByTime)Snapshot of recent fills, then each new fill
orderUpdates{"type":"orderUpdates","user":"0x…"}Order status changes for that account
userEvents{"type":"userEvents","user":"0x…"}Fills, funding, liquidations and non-user cancels. Arrives on channel: "user".
userFundings{"type":"userFundings","user":"0x…"}Hourly funding payments
clearinghouseState{"type":"clearinghouseState","user":"0x…"}Margin summary and open positions

Two details catch people out. First, userEvents messages arrive on channel: "user", not "userEvents", so a router keyed on the subscription type misses them. Second, user feeds need the real account address (master or sub-account). The docs warn that passing an API (agent) wallet address gives you an empty result.

What we measured on mainnet

We ran the script below against mainnet on 20 October 2026, around 07:10 UTC. These are observations from short windows of 15 to 30 seconds, not guarantees. Cadence depends on market activity.

FeedObserved
l2Book BTC (default)20 levels per side, one push about every 4.5 to 5 seconds
l2Book BTC with fast: true5 levels per side, one push about every 0.55 seconds
bbo BTC204 messages in 30 seconds (about 7 per second)
trades BTCA batch about every 0.85 seconds, 75 trades in 20 seconds
activeAssetCtx BTCAbout one update per second. Sample: mark 81,800, oracle 81,820, funding 0.0000125 per hour, open interest 39,965 BTC
allMidsAbout every 5 seconds, 1,355 keys including 472 spot pairs written as @index
userFillsFirst message isSnapshot: true with 30 fills, then streaming messages with no isSnapshot field

The takeaway: if you need top of book fast, use bbo or l2Book with fast: true. The default 20-level book is fine for depth charts but too slow for anything latency-sensitive. Also note that streaming fill messages simply omit isSnapshot, so check data.get("isSnapshot") rather than comparing it to false.

Snapshots, reconnects and missed data

Hyperliquid says plainly that disconnects "may happen periodically and without announcement", and that every automated client should reconnect. The good news is that you rarely lose data:

  1. When you resubscribe, time-series feeds such as userFills and userFundings send a snapshot first (isSnapshot: true). Deduplicate against what you already stored, for example by (coin, tid) for fills.
  2. For trades, the docs say tid is a 50-bit hash of the buyer and seller order IDs. For a globally unique trade ID, use (block_time, coin, tid).
  3. If the snapshot does not reach back far enough, backfill with the matching REST info request, such as userFillsByTime. Our Hyperliquid historical data guide covers the pagination limits.

A simple, robust pattern is a loop that reconnects with exponential backoff (1 s, 2 s, 4 s, capped at about 30 s), resubscribes everything from a list, and treats the first snapshot after each reconnect as a merge, not a replacement.

Heartbeats: the 60-second rule

The server closes a connection if it has not sent you anything for 60 seconds. A busy feed like bbo never hits that, but a socket subscribed only to userFills for a quiet wallet will. Send {"method":"ping"} about every 50 seconds and expect {"channel":"pong"} back. We confirmed the pong on mainnet. The docs specify this JSON message, so send it rather than relying on your library's protocol-level ping.

Post requests over the WebSocket

You can send the same payloads as the REST API over the socket. Wrap them like this:

{"method": "post", "id": 1,
 "request": {"type": "info",
             "payload": {"type": "clearinghouseState", "user": "0x..."}}}

The reply arrives on channel: "post" with the same id, and response.type is info, action or error. Signed actions use "type": "action" with the usual action, nonce and signature. The id is mandatory, so give every request a unique one to match replies. explorer requests are not supported. In our test, a clearinghouseState post for the HLP vault address returned an account value of about $39.0M on the same socket that was streaming market data.

Limits to keep in mind

WebSocket usage has its own caps, separate from REST weight: 10 connections, 30 new connections per minute, 1,000 subscriptions, 10 unique users across user-specific feeds, 2,000 messages sent per minute and 100 in-flight post requests. The 10-user cap is the one that matters for wallet tracking: one IP cannot stream fills for 50 wallets. Our Hyperliquid rate limits guide has the full breakdown and worked polling examples.

Python example: stream market data and a wallet's fills

It needs only pip install websockets and no keys, since all of this data is public. Replace WALLET with any address you want to watch.

import asyncio, json, time, collections
import websockets  # pip install websockets

WS_URL = "wss://api.hyperliquid.xyz/ws"
WALLET = "0xdfc24b077bc1425ad1dea75bcb6f8158e10df303"  # any public address (here: the HLP vault)

SUBS = [
    {"type": "allMids"},
    {"type": "trades", "coin": "BTC"},
    {"type": "bbo", "coin": "BTC"},
    {"type": "l2Book", "coin": "BTC"},
    {"type": "candle", "coin": "BTC", "interval": "1m"},
    {"type": "userFills", "user": WALLET, "aggregateByTime": True},
]

async def heartbeat(ws):
    while True:                      # server drops sockets silent for 60 s
        await asyncio.sleep(50)
        await ws.send(json.dumps({"method": "ping"}))

async def run(seconds=30):
    counts = collections.Counter()
    async with websockets.connect(WS_URL, max_size=None) as ws:
        for s in SUBS:
            await ws.send(json.dumps({"method": "subscribe", "subscription": s}))
        # one-off info request over the same socket
        await ws.send(json.dumps({"method": "post", "id": 1,
            "request": {"type": "info", "payload": {"type": "clearinghouseState", "user": WALLET}}}))
        hb = asyncio.create_task(heartbeat(ws))
        end = time.time() + seconds
        while time.time() < end:
            msg = json.loads(await asyncio.wait_for(ws.recv(), timeout=60))
            ch, data = msg["channel"], msg.get("data")
            counts[ch] += 1
            if ch == "userFills" and data.get("isSnapshot"):
                print("userFills snapshot:", len(data["fills"]), "fills")
            elif ch == "bbo" and counts[ch] == 1:
                bid, ask = data["bbo"]
                print("BTC bbo:", bid["px"], "/", ask["px"])
            elif ch == "post":
                st = data["response"]["payload"]["data"]
                print("account value:", st["marginSummary"]["accountValue"])
        hb.cancel()
    print(dict(counts))

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

On our run it printed the BTC best bid and offer (81,803 / 81,805), the fills snapshot, the account value from the post request, and a count of messages per channel. To use it in production, wrap run() in a reconnect loop with backoff and write messages to a queue instead of printing them.

When you do not need the WebSocket at all

Building your own stream makes sense for bots, market-making and research. If what you want is "how is this wallet doing", it is a lot of plumbing: reconnects, dedupe, the 10-user cap, funding and fee accounting. Hyperfolio is a free, read-only PWA (an installable web app) that tracks Hyperliquid wallets with PnL, positions, funding, Smart Money and Markets views, without API keys or signatures. See how tracking without API keys works and how to track several wallets at once.

FAQ

What is the Hyperliquid WebSocket URL?

wss://api.hyperliquid.xyz/ws on mainnet and wss://api.hyperliquid-testnet.xyz/ws on testnet.

Do I need an API key to use the Hyperliquid WebSocket?

No. Market and user feeds are public and need no authentication. You only sign when you send actions (orders, cancels) through a post request.

Why does my Hyperliquid WebSocket disconnect after a minute?

The server closes connections it has not sent a message to in 60 seconds. Send {"method":"ping"} regularly on quiet subscriptions.

Why are my userEvents messages missing?

They arrive on channel: "user". Also check that you used the account address, not an agent wallet address.

How fast is the Hyperliquid l2Book feed?

In our test the default 20-level BTC book updated about every 4.5 to 5 seconds, while fast: true (5 levels) updated about every 0.55 seconds. For top of book, bbo sent about 7 messages per second.

Can I place orders over the WebSocket?

Yes. Send the signed action inside {"method":"post","request":{"type":"action",...}}. The response mirrors the REST exchange endpoint.

Want the result without the plumbing? Paste a wallet into Hyperfolio and see live PnL and positions with no API keys. Trading on Hyperliquid? Use code HYPERFOLIO for 4% off fees (how the referral works).

Try Hyperfolio for free

Track your Hyperliquid portfolio in real time with PnL, Smart Money, Markets, Perp Calculator, multi-venue portfolio and push alerts.

HYPERLIQUID

-4% fees

Trade on Hyperliquid · 4% off fees

Hyperfolio is an independent app — no ads, no commissions, no sponsors. Referrals are our only funding; we truly appreciate you trading through our link.

Use referral

Free for you · keeps Hyperfolio running

Related articles

Guide

Hyperliquid Rate Limits 2026: REST, WebSocket & Address Caps

Hyperliquid rate limits explained: 1,200 REST weight per minute per IP, info weights 2/20/60, WebSocket caps (10 connections, 10 users), address-based action budgets, and how to track wallets without burning your quota.

Guide

Hyperliquid Historical Data 2026: Candles, Funding & S3

Where to get Hyperliquid historical data: candleSnapshot candles and the 5,000-candle limit, hourly funding history with pagination, your own fills, the S3 archives, rate limits and a Python script that exports candles to CSV.

Guide

Hyperliquid Open Interest 2026: Read OI, Caps & API

What open interest means on Hyperliquid, how to convert it to USD, how to read OI with price and funding, why orders get rejected at the open interest cap, HIP-3 caps, and a Python script to pull OI from the public API.

Guide

Hyperliquid Testnet Guide 2026: Faucet, API & Chain ID

How to use Hyperliquid testnet: claim 1,000 mock USDC from the faucet (mainnet deposit required), fix the email-login address issue, switch the API and Python SDK, and add HyperEVM testnet (chain ID 998).

Guide

Hyperliquid TP/SL Guide 2026: Take Profit & Stop Loss

How Hyperliquid TP/SL works: mark-price triggers, 10% market slippage tolerance, limit exits, position vs parent-order (OCO) attachment, API grouping, and common fill mistakes.

Guide

Hyperliquid TWAP Orders Guide 2026: Duration, Slippage & API

How Hyperliquid TWAP works: 5 minutes to 7 days, $100 minimum, 30s interval floor, 3% suborder slippage, ±20% randomize, catch-up caps, and the twapOrder / twapCancel API.