WebSocket overview
All streaming happens over a single endpoint:
wss://api.staging.sphx.io/v1/ws
The HTTP request is a standard Upgrade: websocket handshake. Public
channels work anonymously; private channels unlock by presenting your
bearer token on the upgrade request. Browser clients, whose
WebSocket API cannot set headers, may pass the same credential as
?token=your_api_key. The header wins when both are present. Prefer
the header outside browsers: intermediaries log query strings.
The protocol
Both directions speak small JSON frames:
{ "op": "subscribe", "channel": "orderbook_delta", "params": { "instrument_id": 1000 } }
{ "op": "unsubscribe", "channel": "orderbook_delta", "params": { "instrument_id": 1000 } }
{ "op": "ping" }
{ "channel": "orderbook_delta", "event": "book_changed", "data": { "...": "..." } }
{ "event": "subscribed", "channel": "orderbook_delta" }
{ "event": "error", "data": { "...": "..." } }
Heartbeats
The server sends {"event":"ping"} every 15 seconds. Reply (any frame
counts as liveness) or the connection is closed. Treat a silent socket
older than 30 seconds (two missed pings) as dead: reconnect and
resubscribe.
Channels
| Channel | Access | Params | Events |
|---|---|---|---|
orderbook_delta | public | instrument_id | book_changed — per-level {side, price_pips, new_count} |
trade | public | instrument_id | trade — the anonymized tape |
candles | public | instrument_id, resolution | update (open bar), closed (bar sealed) |
mark | public¹ | instrument_id | mark — mark-price publications |
market_lifecycle | public | — | instrument_lifecycle, account_state_change |
fill | private | — (scoped to your account) | fill |
account | private | — (scoped to your account) | balance, margin, settlement, withdrawal, and liquidation events |
¹ mark is enabled per instrument by venue configuration.
Each channel has its own reference page in the sidebar — exact message schemas and example frames, generated from the venue's AsyncAPI document. Start with protocol & session frames.
Reconnecting
On reconnect: resubscribe every channel, then reconcile state you care
about via REST (GET /v1/snapshot, GET /v1/orders) — deltas missed
while disconnected are not replayed. The candles channel re-sends the
current open bar on subscribe, and orderbook_delta is preceded by a
full-book bootstrap frame, so those two self-heal.