Skip to main content
Live prices over a WebSocket. This section is the contract — everything below is what the server actually sends.

Connecting

?pairs= is optional; omit it to receive every asset. No authentication, though the handshake is capped — see Limits. Browser clients are subject to an origin allowlist; a rejected upgrade is closed with a bare 403.

Server messages

One snapshot on connect, carrying the seq baseline for every asset:
Then one frame per tick:
data is byte-identical to a row from GET /v1/pricesseq sits on the frame, not inside it, so the tick payload stays the same shape as REST.

Client messages

Every message gets exactly one reply — an ack carrying pairCount (the number of assets you now receive; null is all of them, 0 is none), or an error carrying a code of malformed, unknown_type, invalid_pairs, no_filter or rate_limited.
Filters only widen. subscribe on an unfiltered connection is a no-op acked with "pairCount": null — to narrow, reconnect with ?pairs=. unsubscribe needs a filtered connection and is rejected with no_filter otherwise. Unsubscribing your last asset leaves you receiving nothing ("pairCount": 0), which subscribe recovers without reconnecting.

Detecting loss

Delivery is best effort, but loss is detectable. If more than 1 MB is buffered for a slow client, ticks are dropped rather than queued — and you are told:
seq is a per-asset counter, so a jump between consecutive tick frames for one asset means you missed that many. Baseline each asset from the snapshot, then compare. Two limits on what seq can tell you. It is per connection — process-wide, not global, so it does not survive a reconnect and is not comparable across replicas. And there is no replay: the stream tells you that you fell behind, not what you missed, so recover by re-reading GET /v1/prices. Assets with no trading schedule are omitted entirely, from the snapshot and from ticks. A missing asset is not a signal that its market is closed.

Limits

Per pod, so the effective ceiling scales with replica count. Read the headers on a rejected upgrade rather than hard-coding these. Rejected upgrades carry Retry-After.

Heartbeat

The server pings every 30 seconds and terminates a connection that has not ponged since the previous ping. Browsers pong automatically; other clients need a library that does. On shutdown the server closes with 1001 going away after draining.