WebSocket market stream
Subscribe to trades, tickers, and candles, then recover correctly after gaps or reconnects.
Connect to the market stream for updates on selected pairs:
wss://launch-api.supanova.app/api/v1/market/wsThe stream is read-only. It does not submit orders or confirm a private account's settlement.
Authenticate the connection
Pass your read key in the WebSocket upgrade's X-API-Key header. Keys in query strings are rejected. Never append ?api_key=… to the URL.
The browser's native WebSocket API cannot set an arbitrary authentication header. For a keyed integration, connect from your server and expose only the data your frontend needs. Do not ship your customer key to the browser.
Subscribe to channels
Read a real pair_id from the Market API, then send one subscription frame:
{
"op": "subscribe",
"channels": [
"trades.<pair_id>",
"ticker.<pair_id>",
"candles.1m.<pair_id>"
]
}Replace <pair_id> with the complete value, including its :curve or :amm suffix.
| Channel | Data |
|---|---|
trades.<pair_id> | Trade rows, oldest first. The initial snapshot contains the latest 20. |
ticker.<pair_id> | Pair statistics, updated on new fills and at least every 30 seconds. |
candles.<interval>.<pair_id> | The forming candle. A closed candle is sent again with closed: true. |
Unsubscribe with the same channel names and "op": "unsubscribe". Send {"op":"ping"} for an application-level ping; the server responds with {"op":"pong"}.
Read messages
Data messages have this structure:
{
"channel": "ticker.<pair_id>",
"type": "snapshot",
"seq": 1,
"ts": 0,
"data": {}
}This is a structural example: ts and data are populated by the stream. type is snapshot or update. Control messages use op, including subscribed, unsubscribed, pong, and error.
Node.js connection example
Install ws, set SUPANOVA_API_KEY and SUPANOVA_PAIR_ID in the environment, and run this as an ES module:
import WebSocket from "ws";
const apiKey = process.env.SUPANOVA_API_KEY;
const pairId = process.env.SUPANOVA_PAIR_ID;
if (!apiKey || !pairId) throw new Error("Set API key and pair ID first");
const socket = new WebSocket("wss://launch-api.supanova.app/api/v1/market/ws", {
headers: { "X-API-Key": apiKey },
});
socket.on("open", () => {
socket.send(JSON.stringify({
op: "subscribe",
channels: [`ticker.${pairId}`, `trades.${pairId}`],
}));
});
socket.on("message", (bytes) => {
const message = JSON.parse(bytes.toString());
if (message.op === "error") {
console.error(message.code, message.message);
return;
}
if (message.channel) console.log(message.channel, message.seq, message.data);
});
socket.on("error", (error) => console.error("Stream error:", error.message));
socket.on("close", (code) => console.log("Stream closed:", code));This example prints messages. A production consumer also needs the reconciliation and reconnect behavior below.
Recover missing data
Track seq separately for each channel. It increases by one per message on that channel. A gap means your local state may be incomplete.
For trades, fetch recent trades over REST, page back until the last processed trade ID, and deduplicate by id. A trade can appear in both the initial snapshot and the first update. Do not count it twice.
After reconnecting, take a fresh snapshot and reconcile it with your stored state. Resubscribe only to the channels you need. Use exponential backoff with jitter so a service interruption does not create a reconnect loop.
Limits and close codes
Read the subscription limit from GET /api/v1/market/info. Inbound frames are limited to 4 KiB and 10 frames per second. Batch related subscriptions in a single frame.
| Code | Response |
|---|---|
4401 | The key was revoked or expired. Stop reconnecting with it and replace or renew the credential. |
1013 | The consumer could not keep up. Reduce work in the message handler, reconcile through REST, and reconnect after a delay. |
The server also uses WebSocket ping/pong to detect dead connections. A client library should respond to protocol pings; do not disable that behavior. A keyed connection closes within about five seconds of revocation or expiry.