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.
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 theirchannelfield. 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 asuserFillsstart with a snapshot taggedisSnapshot: true. You can also sendinforequests 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
- Mainnet:
wss://api.hyperliquid.xyz/ws - Testnet:
wss://api.hyperliquid-testnet.xyz/ws(see our Hyperliquid testnet guide)
Everything is JSON text frames. There are four client methods:
| Method | Example | Server reply |
|---|---|---|
subscribe | {"method":"subscribe","subscription":{"type":"trades","coin":"BTC"}} | channel: "subscriptionResponse", then data on channel: "trades" |
unsubscribe | Same object as the original subscription | Feed 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:
| Feed | Subscription | What 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.
| Feed | Observed |
|---|---|
l2Book BTC (default) | 20 levels per side, one push about every 4.5 to 5 seconds |
l2Book BTC with fast: true | 5 levels per side, one push about every 0.55 seconds |
bbo BTC | 204 messages in 30 seconds (about 7 per second) |
trades BTC | A batch about every 0.85 seconds, 75 trades in 20 seconds |
activeAssetCtx BTC | About one update per second. Sample: mark 81,800, oracle 81,820, funding 0.0000125 per hour, open interest 39,965 BTC |
allMids | About every 5 seconds, 1,355 keys including 472 spot pairs written as @index |
userFills | First 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:
- When you resubscribe, time-series feeds such as
userFillsanduserFundingssend a snapshot first (isSnapshot: true). Deduplicate against what you already stored, for example by(coin, tid)for fills. - For trades, the docs say
tidis a 50-bit hash of the buyer and seller order IDs. For a globally unique trade ID, use(block_time, coin, tid). - If the snapshot does not reach back far enough, backfill with the matching REST
inforequest, such asuserFillsByTime. 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% feesTrade 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.
Free for you · keeps Hyperfolio running
Related articles
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.
GuideHyperliquid 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.
GuideHyperliquid 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.
GuideHyperliquid 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).
GuideHyperliquid 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.
GuideHyperliquid 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.