Skip to content

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.

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.

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.

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.

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.

{
"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 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.

{
"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.

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:

Terminal window
node websocket-client.mjs

Private 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.

Account access

Jurisdiction Restriction

Trade8 is not available in your jurisdiction.

You can still explore the product and read the documentation.

Explore the Docs