Documentation Index
Fetch the complete documentation index at: https://stabyl.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
The Stabyl WebSocket API streams real time exchange data and account specific trading updates. Use REST to discover markets and fetch durable baseline state, then use WebSocket subscriptions for low latency updates.
Endpoints
| Environment | URL |
|---|
| Production | wss://api.stabyl.com/v1/ws |
| Staging | wss://api-staging.stabyl.com/v1/ws |
Authentication
Market data topics can be used without account authentication. Private topics and order commands require an API key:
wss://api-staging.stabyl.com/v1/ws?token=<api_key>
Because the API key is carried in the WebSocket URL, connect from server side
automation only. Do not open API-key WebSocket connections from browser,
mobile, desktop, or user controlled code, and do not log full WebSocket URLs.
Pair and Topic Naming
REST endpoints use the pair identifier returned by GET /partner/exchange/markets, such as USD/NGN.
WebSocket topic names use a topic-safe pair key where / becomes _:
| REST pair ID | WebSocket pair key | Example topic |
|---|
USD/NGN | USD_NGN | orderbook.USD_NGN |
Today, USD/NGN is the supported exchange pair. When more pairs are added, discover them from GET /partner/exchange/markets and derive the topic key by replacing / with _.
Use the REST pair ID in command payloads, such as "pair_id": "USD/NGN". Use the topic key only inside topic names, such as trades.USD_NGN.
Message Model
All client and server messages are JSON objects with a type field.
Client messages:
| Type | Purpose |
|---|
subscribe | Subscribe to one or more topics |
unsubscribe | Stop receiving one or more topics |
ping | Optional application-level heartbeat |
command | Submit an authenticated order command |
Server messages:
| Type | Purpose |
|---|
subscribed | Confirms active topics and returns optional snapshots |
unsubscribed | Confirms topics were removed |
data | Carries a topic update |
pong | Responds to application-level ping |
error | Reports a subscription, auth, rate, or format error |
command_ack | Confirms an order command was accepted for processing |
command_error | Reports an order command rejection |
Basic Session
Connect From A Server
import WebSocket from "ws";
const baseUrl =
process.env.STABYL_WS_URL ?? "wss://api-staging.stabyl.com/v1/ws";
const apiKey = process.env.STABYL_API_KEY;
const ws = new WebSocket(`${baseUrl}?token=${encodeURIComponent(apiKey)}`);
ws.on("open", () => {
ws.send(
JSON.stringify({
type: "subscribe",
topics: ["orderbook.USD_NGN", "trades.USD_NGN", "user.orders"],
snapshot: true,
}),
);
});
ws.on("message", (raw) => {
const message = JSON.parse(raw.toString());
if (message.type === "data") {
console.log(message.topic, message.seq, message.data);
}
});
Reliability Model
Every data message includes:
| Field | Meaning |
|---|
topic | Topic that produced the update |
seq | Monotonic sequence for that topic within the current epoch |
epoch | Stream epoch; changes after some server restarts |
ts | Server event timestamp |
data | Topic-specific payload |
Store the latest {epoch, seq} per exact topic. On reconnect, send those values in resume. Resume is best-effort; if the server cannot replay from your sequence, rebuild state from REST and continue with fresh WebSocket updates.
Use exact topics when you need reliable resume. Wildcard topics are useful for broad monitoring, but exact topics give cleaner recovery behavior.
Operational Limits
Keep each connection focused on the streams it needs:
| Limit | Guidance |
|---|
| Subscriptions | Up to 50 active topics per connection |
| Message size | Keep command and subscription frames compact JSON objects |
| Reconnects | Use exponential backoff with jitter after disconnects or rate_limited errors |
| State rebuilds | Prefer REST reads for durable state, then use WebSocket for live changes |
If a connection receives rate_limited, slow down, reduce the subscription set if needed, and reconnect only after a backoff delay.