WebSockets
The exchange pushes real-time updates over a WebSocket endpoint at /ws on the same host as the REST API:
wss://api.testnet.mnx.fi/ws (testnet)
wss://api.app.mnx.fi/ws (production)The protocol is topic-based publish/subscribe: you send a SUBSCRIBE message listing topics, and the server pushes a JSON message whenever an event occurs on one of them.
Subscribing
const ws = new WebSocket('wss://api.testnet.mnx.fi/ws')
ws.onopen = () => {
ws.send(
JSON.stringify({
type: 'SUBSCRIBE',
topics: ['prices:1', 'market:1:orderbook'],
})
)
}
ws.onmessage = (event) => {
const message = JSON.parse(event.data)
// Server events: { topics: string[], timestamp, data: { type, ... } }
// Control acks: { type: 'SUBSCRIBED' | 'SUBSCRIBE_REJECTED' | ... }
}The server acknowledges with { "type": "SUBSCRIBED", "topics": [...] }. Topics you are not allowed to subscribe to come back in a SUBSCRIBE_REJECTED message instead. Unsubscribe the same way with { "type": "UNSUBSCRIBE", "topics": [...] }. Event messages carry the topics they were published to, a timestamp, and a data object whose type field identifies the event.
Topics
| Topic | Scope | What it carries |
|---|---|---|
prices:<market_id> | Public | Oracle price updates (PRICE_UPDATED). |
funding:<market_id> | Public | Funding rate updates (FUNDING_RATE_UPDATED). Funding is the periodic payment between longs and shorts that keeps a perpetual's price anchored to the underlying. |
market:<market_id>:orderbook | Public | Aggregated order book level deltas and refresh signals. |
market:<market_id>:stats | Public | Aggregate market statistics. |
user:<user_id> | Private | All events for your account across markets: orders, positions, balances, leverage adjustments, liquidations, deposits, and withdrawals. The recommended single subscription for account state. |
deposits:<user_id> | Private | Deposit events only. |
orders:<market_id>:<user_id>, positions:<market_id>:<user_id>, liquidations:<market_id>:<user_id> | Private | Granular per-market slices of your account events. Every user-owned event is also emitted to user:<user_id>, so subscribe to these only when you want narrower streams. |
Authenticating for private topics
Private topics require the same credentials as the REST API (see Authentication & signing), supplied inside the SUBSCRIBE message — either an authorization field with a full header value ("Bearer <token>" or "Key <api-key>") or an authToken field with a bearer token value:
{
"type": "SUBSCRIBE",
"topics": ["user:123"],
"authorization": "Key <api-key>"
}The authenticated user must match the user_id in the topic; otherwise the topic is rejected. Once a connection has authenticated, later SUBSCRIBE messages on the same connection can omit the credential.
Event types
Key data.type values (amounts in WebSocket payloads are raw 1e18-scaled integers — see Getting started):
ORDER_CREATED— an order was accepted, with its id, hash, side, type, price, quantity, and remaining quantity.ORDER_CANCELLED— an order was cancelled, expired, or terminally rejected by batch validation; includes areasonand whether it was partially filled.ORDER_REJECTED— a placement was rejected before acceptance (e.g. insufficient margin).PRICE_UPDATED— a new oracle price for a market.FUNDING_RATE_UPDATED— a new funding rate for a market.POSITION_UPDATED— your position changed on-chain (trade, margin added/removed, or leverage adjusted).LIQUIDATION— a liquidation touched your account (liquidation is the forced closing of a position whose margin no longer covers its risk).ORDERBOOK_LEVEL_DELTA,BATCH_ABORTED,TRADE_BATCH_SETTLED— public order book and batch settlement signals (below).
Keeping an order book in sync
To mirror a market's order book, combine REST snapshots with additive deltas:
- Subscribe to
market:<market_id>:orderbook. - Fetch a snapshot from
GET /v0/orderbook/by-market/:market_id(50 levels per side by default). - Apply
ORDERBOOK_LEVEL_DELTAevents additively on top. Each delta carries adeltaIdfor duplicate-delivery protection and areadAftercoordinate; a snapshot already includes all deltas at or below its sequence, so do not re-apply older deltas after a snapshot lands. - On
BATCH_ABORTED— the coarse refresh signal for paths that cannot be expressed as a precise level delta — refetch the snapshot (using the event'sreadAfter).
Connection lifecycle
- The server sends protocol-level WebSocket ping frames every 30 seconds and drops connections that stop answering. Standard WebSocket clients answer these automatically; no application code is needed. An application-level
{ "type": "PING" }/{ "type": "PONG" }exchange is also available for testing. - Before a planned restart (deploys), the server sends a
SERVER_RESTARTINGmessage and closes with WebSocket close code 1012. Reconnect and re-send your subscriptions — they are not persisted across connections.