Subscriptions
After the system ready frame, send UTF-8 JSON text frames with an action field. The server acknowledges each subscribe and unsubscribe with a matching system event.
To try a subscribe first, use the live playground on the WebSocket overview.
Subscribe
action- The action to run.Values
subscribe channels- Non-empty array, for example
["crypto.quotes", "crypto.trades"]. symbols- Array or comma-separated string. Values are trimmed, uppercased, de-duplicated and sorted before validation.
snapshotfalseskips the initial snapshot and delivers live updates only;"always"replays the cached value even when it is no longer recent. See Initial snapshot.Valuestruefalse"always"Defaulttrueview- Quote and trade channels only.
"full"adds the snapshot record: a replay at any age on every channel, crypto and forex included, andchangeframes. See Full view.Values"default""full"Default"default" spreadetfs.quotesandovernight.quotesonly:"full"shows the spread the market is quoting. US ETFs outside the regular session also get the books wider than 0.5% of the mid (ETFs: full spread); overnight quotes widen to the night spread (Overnight full spread). Any other channel in the same subscribe rejects it.Values"normalized""full"Default"normalized"intervalstocks.aggandetfs.aggonly: which bar intervals to receive for the symbols in this request. A later subscribe for the same symbol replaces its intervals; any other channel rejects it.Values1m5m15m1h4h1dDefault1msessionstocks.aggandetfs.aggonly:extendedadds pre-market and after-hours bars for 1m, 5m and 15m. Any other channel rejects it.ValuesregularextendedDefaultregularrollsfutures.quotesonly, opt-in: adds the roll fields of these methods to every.C1frame. The latest futures subscribe on the connection sets them; a value outside the list fails withINVALID_ROLLS. See Futures CFDs: roll methods.Valuesr5r5ccyclenoticeall
{
"action": "subscribe",
"channels": ["crypto.quotes", "crypto.trades"],
"symbols": ["BTCUSD", "ETHUSD"]
}{
"type": "system",
"event": "subscribed",
"channels": ["crypto.quotes", "crypto.trades"],
"symbols": ["BTCUSD", "ETHUSD"]
}Settled OHLCV bars on stocks.agg follow the bar contract in Stocks: OHLCV bars over WebSocket, and etfs.agg carries the same bars for US-listed ETFs (ETFs: OHLCV bars over WebSocket).
{
"action": "subscribe",
"channels": ["stocks.agg"],
"symbols": ["US:KO", "US:JPM"],
"interval": ["1m", "5m"],
"session": "extended"
}Initial snapshot
Right after subscribed, the stream sends the last known quote or trade for each subscribed symbol that has a recent value, so you can show a price before the next tick. Symbols without a recent value are skipped until their first live tick.
- Same shape as live
- Crypto, forex, stocks, perpetuals and futures CFDs use
tsand string numbers; indices, ETFs and commodities usetimestampand numbers. Snapshot frames stay marked, so you can drop them. - Event time
- The time field is when the market event happened, not the send time. Outside trading hours it can be older than the clock; compare it with your own receive time.
| Channel | Snapshot on subscribe |
|---|---|
| Crypto and forex | Nothing: both stream continuously, so the next live update comes first. "view": "full" replays the last quote and trade. |
| Most other quote and trade channels | The last value, when it is recent. |
| US and Asian equities, ETFs | Only while the value is recent, about two minutes; outside the session nothing arrives until the first tick. "snapshot": "always" replays the last value anyway. |
stocks.status | Never: a halt status is about the present, so only events after you subscribe arrive. |
stocks.agg | Never: the first settled bar arrives within one interval. Backfill with GET /stocks/agg. |
etfs.agg | Never, like stocks.agg. History is on GET /etfs/agg. |
bonds.quotes | Always the latest daily rate, whatever its date; only "snapshot": false suppresses it. |
futures.quotes | Always the last price of each month or series, held or live (the live flag says which); only "snapshot": false suppresses it. |
"trade_mode": "full_volume" | Never, whatever snapshot says: a replayed trade is not an execution and would add size that never happened. |
Use "snapshot": "always" while developing against a closed market. Shortly after a service restart in a closed session there may be nothing to replay yet; GET /stocks/snapshot/{symbol} returns a last known value at any hour.
{
"action": "subscribe",
"channels": ["stocks.quotes", "stocks.trades"],
"symbols": ["US:KO"],
"snapshot": "always"
}Use "snapshot": false when a cached value must never count as current, for example an on-chain oracle at session open: the first frame is then a genuine live update.
{
"action": "subscribe",
"channels": ["indices.quotes", "indices.trades"],
"symbols": ["JP225", "HK50"],
"snapshot": false
}Full view
Add "view": "full" to a quote and trade subscribe to stream everything GET /{asset}/snapshot returns, with no REST call. Quotes and trades keep their usual frames on the same connection.
- On subscribe
- The last quote and trade of every symbol at any age, crypto and forex included, then one
changeframe per symbol, all marked"snapshot": true. Commodities replay a quote from the last day and no trade. A symbol with nothing yet starts with its first live update. - Then
- A
changeframe wheneverprev_close,changeorchange_percentmoves, against the same previous close and with the same arithmetic as the snapshot endpoint. Where it would first ask a vendor (an ETF before the US open or on a closed day, an index whose live feed has stalled), the stream measures from the newest price it holds. - Options
"snapshot": falseskips the replay and the first change value: change frames then arrive only when a value moves. A later subscribe withoutviewreturns its symbols to the default stream.
{
"action": "subscribe",
"channels": ["stocks.quotes", "stocks.trades"],
"symbols": ["US:KO"],
"view": "full"
}{
"type": "system",
"event": "view",
"view": "full",
"symbols": ["US:KO"]
}The frame itself is on Message types. An unknown value never fails the subscribe: it is answered with a view notice carrying a code, and those symbols stay on the default stream.
| code | Meaning |
|---|---|
INVALID_VIEW | The value is not "full" or "default". |
VIEW_UNAVAILABLE | A market wildcard: the full view applies to individual symbols. |
VIEW_LIMIT | No change values for these symbols on this subscribe; subscribe again to retry. Their quotes and trades are unaffected. |
Unsubscribe
Same channels and symbols rules as subscribe. It removes the symbols from the listed channels on this connection; subscriptions on other channels stay.
{
"action": "unsubscribe",
"channels": ["crypto.quotes"],
"symbols": ["BTCUSD"]
}{
"type": "system",
"event": "unsubscribed",
"channels": ["crypto.quotes"],
"symbols": ["BTCUSD"]
}Ping and pong
- Client ping
- Send
{ "action": "ping" }; the server answers with apongevent carrying itsts. - Server ping
- The server sends native WebSocket ping frames. A client that stops answering gets
{ "type": "system", "event": "disconnect", "code": "HEARTBEAT_TIMEOUT" }and a close with code4008.
{
"type": "system",
"event": "pong",
"ts": 1743512400000
}Subscription limits
- Symbols per subscribe
- Up to 500 in one message. A server cap, not a plan field: send more messages for more symbols.
- Distinct symbols
- Your plan sets how many different symbols you can stream at once, across all connections and channels. The dashboard shows usage.
- Channels
- Only the asset classes in your active packages. Add or upgrade a package to unlock more.
Defaults by plan are on the Rate limits page.