Hyperliquid WebSocket API

Stream trades, order books and account events instead of polling, with a 25-line script we ran against mainnet.

Updated Fact-checked November 4, 2026 6 sources
Short answer

Hyperliquid's WebSocket lives at wss://api.hyperliquid.xyz/ws and needs no key to read. Send {"method":"subscribe","subscription":{"type":"trades","coin":"BTC"}} and trades stream in as they print. Each IP may hold 10 connections and 1,000 subscriptions, with at most 10 different users across user feeds. The server closes a connection that has had no message for 60 seconds, so send {"method":"ping"} on quiet feeds.

Polling the info endpoint works, but it burns rate-limit weight and you only ever see snapshots. The WebSocket pushes changes as they happen. It is also the only sensible way to watch more than a handful of markets or accounts at once: Hyperliquid’s own docs say “use websockets for lowest latency realtime data” (rate limits).

Connect and subscribe

NetworkURL
Mainnetwss://api.hyperliquid.xyz/ws
Testnetwss://api.hyperliquid-testnet.xyz/ws

Every subscription is one JSON message:

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

The server confirms it with a subscriptionResponse echoing what you asked for, then sends data messages whose channel names the feed:

{"channel":"subscriptionResponse","data":{"method":"subscribe",
 "subscription":{"type":"trades","coin":"BTC"}}}
{"channel":"trades","data":[{"coin":"BTC","side":"A",
 "px":"80092.0","sz":"0.24966","time":1793825109482,
 "hash":"0x5c99…14cf","tid":304132077357146,
 "users":["0xa876…eb29","0x298c…5f5a"]}, …]}

That is a real message from mainnet, 4 November 2026, 20:45 UTC. side is A (ask, a sell hit the bid) or B (bid, a buy lifted the ask). users gives the buyer and seller addresses. Unsubscribe by sending the same subscription with "method": "unsubscribe" (subscriptions).

A working Node.js example

Node 22 and later ship a built-in WebSocket, so this needs no packages. It subscribes to three feeds (BTC trades, the ETH book, and the fills of a public HLP sub-vault), prints the first two messages of each, then exits after 12 seconds. We ran it on 4 November 2026.

// node ws-test.mjs  (Node 22+)
const ws = new WebSocket('wss://api.hyperliquid.xyz/ws');
const USER = '0x010461c14e146ac35fe42271bdc1134ee31c703a';
const seen = {};
ws.onopen = () => {
  for (const s of [
    { type: 'trades', coin: 'BTC' },
    { type: 'l2Book', coin: 'ETH' },
    { type: 'userFills', user: USER },
  ]) ws.send(JSON.stringify({ method: 'subscribe', subscription: s }));
  // keep quiet connections alive (server drops after 60s)
  setInterval(() => ws.send('{"method":"ping"}'), 50_000);
};
ws.onmessage = (e) => {
  const m = JSON.parse(e.data);
  seen[m.channel] = (seen[m.channel] ?? 0) + 1;
  if (seen[m.channel] <= 2) console.log(JSON.stringify(m).slice(0, 300));
};
setTimeout(() => { console.log(seen); process.exit(0); }, 12_000);

On our first run the last line was this (the counts change with market activity):

{ subscriptionResponse: 3, trades: 19, l2Book: 4, userFills: 12 }

The same in Python

Tested with the websockets package (version 16.0). It reads three HYPE trade messages, then checks the heartbeat.

# pip install websockets
import asyncio, json, websockets

async def main():
    async with websockets.connect("wss://api.hyperliquid.xyz/ws") as ws:
        await ws.send(json.dumps({"method": "subscribe",
            "subscription": {"type": "trades", "coin": "HYPE"}}))
        for _ in range(3):
            msg = json.loads(await ws.recv())
            print(msg["channel"], json.dumps(msg["data"])[:120])
        await ws.send(json.dumps({"method": "ping"}))
        while json.loads(await ws.recv())["channel"] != "pong":
            pass
        print("pong")

asyncio.run(main())
subscriptionResponse {"method": "subscribe", "subscription": …}
trades [{"coin": "HYPE", "side": "A", "px": "83.511", "sz": "3.03", …
trades [{"coin": "HYPE", "side": "B", "px": "83.511", "sz": "1.79", …
pong

The official Python SDK has a fuller client, websocket_manager.py, with reconnects and a ping thread. Use it if you’re already on the SDK.

The feeds you’ll use most

typeParametersWhat arrives
tradescoinEvery trade: price, size, side, buyer and seller
l2Bookcoin, optional nSigFigs, mantissa, fastBook snapshots, 20 levels a side (5 if fast)
bbocoinBest bid and offer, only when it changes on a block
allMidsoptional dexEvery mid price; core dex by default
candlecoin, intervalThe live candle, 1m to 1M
activeAssetCtxcoinMark, mid, oracle price, funding, open interest, 24h volume
allDexsAssetCtxsnoneContexts for every market on every perp dex
userFillsuser, optional aggregateByTimeA snapshot of recent fills, then each new fill
orderUpdatesuserOrder placed, filled, cancelled
userEventsuserFills, funding, liquidations, non-user cancels; channel is user
clearinghouseStateuser, optional dexPositions and margin for one perp dex
userFundingsuserHourly funding payments
notificationuserText notification messages for the account

There are 24 subscription types in total, including webData3 (what the official front end uses), TWAP feeds, spotState and outcomeMetaUpdates for HIP-4 (full list). For spot markets, use the pair’s @index as the coin, e.g. @107 for HYPE/USDC.

Snapshots on subscribe

Feeds that are a time series (fills, fundings, ledger updates) start with a snapshot of recent history, tagged "isSnapshot": true. Our userFills test got one snapshot message, then live fills. If you persist fills, de-duplicate them: Hyperliquid’s docs say the globally unique trade key is (block time, coin, tid).

User feeds take the account address

Subscribe with the master account or sub-account address you want to watch. An API wallet’s address returns nothing, because API wallets hold no funds; they only sign (docs).

Limits per IP address

From Hyperliquid’s rate limits page:

LimitValue
Open WebSocket connections10
New connections per minute30
Subscriptions1,000
Unique users across user-specific subscriptions10
Messages you send per minute (all connections)2,000
Simultaneous in-flight post requests100

The 10-user cap matters most for trackers. A whale tracker watching 50 wallets can’t subscribe to all of them from one IP. It has to poll clearinghouseState (weight 2 each) for the rest, or use a third-party data provider.

Keep the connection alive

  • Heartbeat. The server closes any connection it hasn’t sent a message on for 60 seconds. Send {"method":"ping"} and the server answers {"channel":"pong"} (heartbeats). A busy trades feed never goes quiet, but a user feed on an idle account does.
  • Reconnect. “Disconnection from API servers may happen periodically and without announcement” (WebSocket). On close, reconnect with backoff and send every subscription again. The new snapshot covers what you missed. For anything else, fill the gap with the matching info request, e.g. userFillsByTime.
  • Stay under 30 new connections a minute. A reconnect loop without backoff hits that limit within seconds.

Sending requests over the socket

You can also send info requests and signed actions over the same connection, which saves an HTTPS round trip:

{ "method": "post", "id": 1,
  "request": { "type": "info",
               "payload": { "type": "l2Book", "coin": "BTC" } } }

The reply comes on the post channel with your id. Use a different id for every request so you can match the replies. For "type": "action", the payload is exactly the signed body you would POST to /exchange, so use an SDK to build it. explorer requests are not supported over WebSocket (post requests).

Trading on Hyperliquid is leveraged, and a socket that silently stops updating can leave a bot acting on stale prices. Check the time on every book message and stop trading when it falls behind. Hyperliquid’s terms restrict the United States, Ontario and other places.

This socket covers HyperCore, the exchange. For HyperEVM smart contracts, see HyperEVM RPC and chain settings: the public RPC there has no WebSocket at all.

Questions people ask

What is the Hyperliquid WebSocket URL?

Mainnet is wss://api.hyperliquid.xyz/ws and testnet is wss://api.hyperliquid-testnet.xyz/ws. Reading public and per-address feeds needs no key.

How many WebSocket connections does Hyperliquid allow?

Per IP address: 10 open connections, 30 new connections per minute, 1,000 subscriptions, 10 unique users across user-specific subscriptions, 2,000 messages sent per minute and 100 in-flight post requests.

Why does my Hyperliquid WebSocket disconnect?

The server closes any connection it hasn't sent a message on for 60 seconds, so quiet feeds need a {"method":"ping"} heartbeat. Hyperliquid also says servers may disconnect without warning, so every client must reconnect and resubscribe.

Can I place orders over the Hyperliquid WebSocket?

Yes. Wrap a signed exchange action in {"method":"post","id":n,"request":{"type":"action","payload":…}}. The signing is the same as for HTTP, so use an SDK. Info requests can be posted the same way with type "info".

Sources

  1. Hyperliquid Docs — WebSocket (URLs, reconnecting)
  2. Hyperliquid Docs — WebSocket subscriptions (types and data formats)
  3. Hyperliquid Docs — WebSocket post requests
  4. Hyperliquid Docs — Timeouts and heartbeats
  5. Hyperliquid Docs — Rate limits and user limits (WebSocket limits)
  6. hyperliquid-python-sdk — websocket_manager.py (reference client)