Streaming
WebSocket: OHLCV
Live candle bars pushed as they’re built — mid-bar updates and final closes for any pool, any timeframe.
WSS
The OHLCV WebSocket emits one frame per bar update for each
Authenticate either via Bearer header (server) or query string (browser) —
Then send a
Server ack:
This first frame is the one-shot, backward-compatible path: a client that only ever sends it sees zero change from before.
Ack with the new totals (now 2 pools × the union of timeframes):
The replace is atomic: if the new set would exceed the per-connection subject cap, the replace is rejected and your prior subscription stays fully intact.
If you’re rendering a live chart, key your local bar store by
Use
(pool, timeframe) you subscribe to. You get mid-bar updates while a bar is open (is_closed: false) and a final update on close (is_closed: true).
This is the right channel when you want to render a live chart or trigger logic on bar close. For one-shot history, use /api/v1/candles and /api/v1/candles/latest. For raw per-swap data, use WebSocket: swaps.
Endpoint and auth
/ws/ohlcv accepts both, same as /ws/swaps:
Subscribe
After the connection is up the server sends a hello frame:subscribe. Both pair_addresses and timeframes are required — the server will reply with {"error": "invalid subscribe: timeframes empty"} (or similar) if either is missing.
pairs is the distinct pool count, subjects is the number of live (pool, timeframe) pairs, and op mirrors status.
A subject is one
(pair_address, timeframe) pair — the unit the stream actually fans out on. A subscribe of 3 pools × 2 timeframes is 3 pairs but 6 subjects.Add / remove / unsubscribe (mid-stream)
Once you’re subscribed you can change the watched symbol set without reconnecting by sending more frames on the same socket. This is what lets a chart or watchlist UI swap the active pool, or add/drop timeframes, in place. Every mutating frame carries anop. Each (pair_address, timeframe) pair it expands to is a subject; the ack’s pairs/timeframes/subjects always report the new total live set after the op — not just the delta.
Add symbols
Remove symbols
Unsubscribe (clear everything)
Replace the whole set
A later frame withop: "subscribe" (or with no op — absent defaults to subscribe) replaces the entire live set with the new one:
Frame shape
Limits and errors
Two independent limits apply:
Mid-stream errors are non-fatal — the socket stays open and a later valid frame still works. (Only an error on the initial subscribe frame closes the connection, as before.)
- Bad frame / unknown op →
{ "error": "invalid subscribe: <detail>" }and the socket stays open. An unknown op comes back as{ "error": "invalid subscribe: unknown op: <op>" }. - Per-frame pool cap →
{ "error": "invalid subscribe: too many pair_addresses; max 1000" }. - Per-connection subject cap → an
addorsubscribe-replace that would push the live set past the cap is rejected atomically — no partial apply, and your existing subjects keep delivering:
Minimal TypeScript client
(pair_address, timeframe, timestamp) and overwrite the entry on every frame for that key — the latest frame is always the freshest snapshot of that bar. When is_closed: true arrives, that bar is final.
Changing the watched set at runtime
A watchlist or chart UI that switches the active pool sendsadd / remove on the same open socket — no reconnect. Helpers, and an ack handler that reads back the new live totals:
op: 'subscribe' (or an op-absent frame) only when you intend to reset the whole set — it discards the current subscription. To widen, always use op: 'add'.
For reconnect strategy, gap-filling against /api/v1/candles, and keepalive guidance, see Reconnect & backpressure.

