Skip to main content

Endpoint

A single WebSocket connection can subscribe to any number of markets. The server pushes a snapshot on subscribe and updates every time the orderbook changes.

Authentication

Browsers can’t set custom headers on the WebSocket handshake, so the signed-request flow moves to query params. The signature covers WS\nPATH\nSORTED_QUERY (excluding key_id/ts/sig)\nTIMESTAMP. See Authentication for the full recipe.
The official SDKs sign the handshake for you.
Signed handshake
For first-party web clients you can authenticate with a Supabase JWT instead: ?access_token=<jwt>. Invalid or missing auth closes the connection with code 4401.

Protocol

All frames are JSON. Client frames are text; server frames are bytes (orjson-serialized UTF-8).

Client → server

  • subscribe / unsubscribe accept one or many river_ids. Duplicate subscribes on the same connection are no-ops.

Server → client

  • snapshot is sent once on successful subscribe when the orderbook is already cached. Subsequent changes are delivered as update frames with an identical payload shape.
  • pending is sent when the orderbook isn’t in cache yet. A snapshot arrives shortly, followed by updates.
  • reconnect is sent during graceful pod shutdown. Reconnect with a small jitter.

Keepalive

Liveness is handled at the WebSocket protocol layer (RFC 6455 PING/PONG control frames). Standard clients (Python websockets, browser WebSocket) reply to server PINGs automatically — no application-level heartbeat is needed.

Orderbook payload

is_valid=false indicates a transient crossed-book state at the exchange. The snapshot that follows will have fresh values.

Rate limits and pacing

Orderbook updates are not rate-capped — every upstream change is forwarded as it happens. If your client falls behind, pending updates are coalesced per market (only the latest state is kept), so you never see stale data. A client that cannot drain fast enough is disconnected with code 1011; reconnect and resubscribe to get a fresh snapshot.

Minimal client

Using the SDK (handles signing automatically):
Or signing the handshake manually:

Close codes

CodeMeaning
1000Normal closure
1011Server-side overflow (client couldn’t keep up with the send rate)
4401Missing or invalid authentication
4503Server draining for deploy — reconnect with jitter