WebSocket Connection Guide
WebSocket base URL: wss://ws.trade8.xyz. Connect to wss://ws.trade8.xyz/v1 for JSON text messages. One connection can carry several subscriptions. Use REST for order submission and account changes; use WebSocket channels to track state and market data.
The WebSocket Channel Reference defines all 12 channels, filters, payload fields, and recovery endpoints.
Connect and Subscribe
Section titled “Connect and Subscribe”Public channels are ticker, orderbook, trades, funding, and venue_status. Subscribe after the connection opens:
{ "op": "subscribe", "id": "btc-book", "channel": "orderbook", "instrument": "BTC-PERP", "venue": "hyperliquid", "settlement_asset": "USDC", "depth": 20}The acknowledgement echoes your request ID and assigns a connection-local subscription ID:
{ "op": "subscribed", "id": "btc-book", "channel": "orderbook", "subscription_id": "sub_example_orderbook", "started_at": "2026-08-31T12:00:00.000Z"}Use a unique id for every command on the connection. A command acknowledgement can arrive between data messages from other subscriptions. Route messages by op, then id or subscription_id.
Authenticate a Private Connection
Section titled “Authenticate a Private Connection”Sign five newline-separated fields: timestamp, WS, /v1, empty idempotency key, empty body. This purpose-specific signature authorizes the WebSocket session. The same 30-second clock window applies.
import { sign } from './rest-client.mjs';
const timestamp = Math.floor(Date.now() / 1000).toString();socket.send(JSON.stringify({ op: 'auth', id: 'session-auth', api_key: process.env.TRADE8_API_KEY, timestamp, signature: sign(process.env.TRADE8_API_SECRET, timestamp, 'WS', '/v1'),}));A successful response contains the granted scopes and accounts:
{ "op": "auth", "id": "session-auth", "success": true, "scopes": ["orders:read", "accounts:read", "custody:read"], "account_ids": ["acct_example_main"]}After that acknowledgement, subscribe to private channels:
{ "op": "subscribe", "id": "main-fills", "channel": "fills", "account_id": "acct_example_main"}Keep credentials in a trusted server process. Public market-data channels can be consumed by a browser. Every private subscription checks the key’s scope and account grant. Revoked or expired keys close the authenticated connection; connect again with an active key after rotation.
Data Envelope
Section titled “Data Envelope”| Field | Meaning |
|---|---|
op |
data for channel messages |
subscription_id |
ID returned in the subscription acknowledgement |
channel |
Channel name |
type |
snapshot or update |
sequence |
Integer starting at 1, increasing by one per subscription message |
previous_sequence |
Previous sequence; 0 on the first message |
timestamp |
Envelope creation time in UTC |
data |
Array of channel records; an empty snapshot clears the previous state |
Channels with snapshots send one complete initial snapshot before updates. A snapshot replaces the subscription’s previous state. Record updates are full replacements, except order-book updates, which replace individual price levels. A zero position quantity closes that position.
Sequences belong to one subscription on one connection. Start fresh after resubscribing. Ignore duplicate or older update sequences. If previous_sequence differs from the last applied sequence, pause dependent decisions and recover that subscription.
Heartbeats
Section titled “Heartbeats”Send an application-level ping every 15 seconds:
{ "op": "ping", "id": "heartbeat-1" }{ "op": "pong", "id": "heartbeat-1", "timestamp": "2026-08-31T12:00:15.000Z"}Reconnect after 45 seconds without an inbound message. Keep heartbeats on a timer independent of market activity. These are JSON messages, so the same mechanism works with Node.js and browser WebSocket clients.
Unsubscribe
Section titled “Unsubscribe”{ "op": "unsubscribe", "id": "stop-book", "subscription_id": "sub_example_orderbook"}The server replies with op: "unsubscribed", the same command id, and the subscription ID. Discard any already-buffered data for that ID after retiring the subscription. Subscribing again creates a new ID and sequence.
Reconnect and Reconcile
Section titled “Reconnect and Reconcile”Reconnect with exponential backoff and jitter, capped at 30 seconds. Reset backoff after a stable connection. Authenticate, restore subscriptions, and wait for fresh snapshots before resuming decisions based on account or book state.
For fills and other event-only channels, subscribe first and buffer incoming records. Page REST history from your last saved timestamp up to the acknowledgement’s started_at. Apply history and buffered events with deduplication, then continue with the live stream. Use an overlapping time window to cover boundary timestamps.
For orders, the initial snapshot contains open orders. Reconcile previously tracked IDs through REST to recover orders that became terminal while disconnected. Keep the highest revision per order. Stream delivery ends with the connection; REST history supplies recovery.
Closing the socket leaves existing orders active. Arm the dead-man switch when your strategy needs an account-wide cancellation timer.
Errors and Close Codes
Section titled “Errors and Close Codes”{ "op": "error", "id": "main-fills", "error": { "code": "SCOPE_DENIED", "message": "This key requires orders:read for the fills channel.", "retryable": false }}| Code | Action |
|---|---|
AUTH_FAILED / close 4001 |
Check credentials, signature, and clock before reconnecting |
SCOPE_DENIED / close 4003 |
Correct account grants or scopes |
INVALID_SUBSCRIPTION |
Correct channel names and required filters |
RATE_LIMITED / close 4008 |
Wait for the message’s retry_after_seconds, then reconnect with backoff |
SLOW_CONSUMER / close 4009 |
Reduce subscriptions or increase processing capacity before reconnecting |
close 1000 |
Normal closure; reconnect if your process still needs the stream |
close 1006 |
Transport interrupted; reconnect and reconcile |
close 1012 or 1013 |
Restart or temporary capacity issue; reconnect with backoff |
An error frame refers to a command; it can leave the connection open. A close code ends the connection. Use account limits for subscription and connection capacity.
Download a Client Example
Section titled “Download a Client Example”The Node.js example subscribes to Hyperliquid’s BTC book, checks sequences, responds to connectivity failures, and reconnects with jitter. Download all three files to the same directory:
node websocket-client.mjsPrivate order subscriptions are opt-in through connect({ privateOrders: true, onOrder, onReconnect }). Provide handlers that persist revisions and reconcile tracked orders. The Order-Book Guide explains the state helper and its gap handling.