Public market data streams over the realtime WebSocket without an account or token. Use a snapshot or history request to establish state, then apply the channel's updates. Connection behavior shared with private channels is described in the WebSocket session model.
Public channels
<symbolId> is the numeric Pair ID from GetSpotConfig. Each channel exists in the
encodings listed; a JSON channel has no suffix unless shown, and a Protobuf channel ends in :proto.
- Order book updates · JSON, Protobuf
One update of a depth tier; Protobuforderbook.v1.OrderBookDelta. - Trades · JSON, Protobuf
An array of trades; Protobufmarketdata.v1.MarketTradeBatch. - Candles · JSON, Protobuf
The current candle of one timeframe; Protobufmarketdata.v1.CandlePoint. - Market overview · Protobuf only
Changed rows of every Pair;marketoverview.v1.MarketOverviewBatch. - Order book heatmap · JSON, Protobuf
One heatmap bucket;marketdata.v1.HeatmapLiveBucket, JSON as ProtoJSON.
The channel names:
public:spot:orderbook:deltas:depth:<depth>:<symbolId>:json
public:spot:orderbook:deltas:depth:<depth>:<symbolId>:proto
public:spot:market:trades:<symbolId>
public:spot:market:trades:<symbolId>:proto
public:spot:market:candles:<timeframe>:<symbolId>
public:spot:market:candles:<timeframe>:<symbolId>:proto
public:spot:market_overview:updates:proto
public:spot:market:heatmap:<interval>:<symbolId>
public:spot:market:heatmap:<interval>:<symbolId>:proto<depth>is a published order book tier:1,20,50,200, or500. Both order book channels need their suffix. See Order book.<timeframe>is a REST candle timeframe such as1m,3m, or1mo. See Candles.<interval>is a heatmap interval:1s,1m,5m, or1h. Read history withHeatmapService.GetOrderbookHeatmap.
Trades are described in Trades.
Connect and subscribe
Official SDKs connect, subscribe, and recover for you. A custom client speaks the Centrifugo client protocol over WebSocket.
Open the connection
Connect to wss://<API host>/connection/websocket, for example wss://api.testnet.polyester.com/connection/websocket. See Environments and hosts. Public
channels need no connection token.
Pick the client protocol
The default protocol is JSON and carries only JSON channels. To receive Protobuf channels, open the
WebSocket with the centrifuge-protobuf subprotocol and use the Protobuf client protocol. A JSON
connection that subscribes to a Protobuf channel is closed with code 3506 when the first binary
publication arrives.
Send connect, then subscribe
Send a connect command, then one subscribe per channel. Public subscriptions need no
subscription token.
{"id":1,"connect":{}}
{"id":2,"subscribe":{"channel":"public:spot:market:trades:1","recover":true}}Answer pings
The connect reply carries "ping":25,"pong":true: the server sends an empty ping about every 25
seconds and expects a reply. In the JSON protocol, answer each {} with {}.
Store the stream position
Each subscribe reply carries the channel's epoch and offset, and each publication its offset.
Keep the latest pair per channel to recover after a reconnect.
The server answered the commands above with:
{"id":1,"connect":{"client":"56cd50db-63da-4728-9e2f-bfe893f717a3","version":"6.6.0 OSS","ping":25,"pong":true}}
{"id":2,"subscribe":{"recoverable":true,"epoch":"1792667891","offset":1621617,"positioned":true,"was_recovering":true}}
{"push":{"channel":"public:spot:market:trades:1","pub":{"data":{"events":[{"symbol":"BTC-USDT","matchId":"2190","side":"SELL","price":"76075.09","qty":"0.00131449","executedAt":"2026-11-05T21:37:52.340853094Z"},{"symbol":"BTC-USDT","matchId":"2191","side":"SELL","price":"76075.08","qty":"0.01307918","executedAt":"2026-11-05T21:37:52.340853094Z"}]},"offset":1621618}}}A subscribe command for a channel name that does not match a pattern above, such as an order book
channel without its :json suffix, is rejected with error 107 (bad request).
Recover after a reconnect
Delivery is at least once. After a reconnect, resubscribe with recover: true and the epoch and offset you stored:
{
"id": 4,
"subscribe": {
"channel": "public:spot:market:trades:1",
"recover": true,
"epoch": "1792667891",
"offset": 1621618
}
}- With
"recovered":true, the reply'spublicationsarray replays what you missed, in order. Apply them as usual, then continue with live publications. - Without
recovered, or with a differentepoch, missed publications could not be recovered. Reload state over REST or ConnectRPC: a new order book snapshot, anafterTradeIdbackfill of trades, or a candle history read. Then continue with live publications.
Channels keep a limited history of recent publications, so a short disconnect usually recovers and a long one does not.
Apply state the same way after a replay as after a live publication: order book updates by book
sequence, trades by matchId, candles by tsSec, and overview rows by symbolId.
Market overview
The market overview is a ticker row per Pair: last price, 24h change, high, low, and volume, the best
bid and ask, and the index price. Read all rows with GET /v1/spot/markets/overview or MarketOverviewService.ListMarketOverview,
then apply updates from public:spot:market_overview:updates:proto.
GET https://api.testnet.polyester.com/v1/spot/markets/overview?symbols=BTC-USDT{
"nextPageToken": "",
"markets": [
{
"symbol": "BTC-USDT",
"lastPrice": "75907.92",
"lastTradeTsNs": "1793913814242553034",
"change24hBps": -19,
"high24h": "76566.37",
"low24h": "75675.89",
"volume24hBase": "6.30453704",
"volume24hQuote": "479470.859709",
"volume24hUsd": "478675.643863",
"bestBid": "75923.1",
"bestBidQty": "0.90602069",
"bestAsk": "75953.48",
"bestAskQty": "0.87235094",
"indexPrice": "75935.234625758",
"listedTsNs": "1793880813827104000",
"sparklines": []
}
]
}POST https://api.testnet.polyester.com/marketoverview.v1.MarketOverviewService/ListMarketOverview
Content-Type: application/json
{"symbolId": [1]}{
"markets": [
{
"symbolId": 1,
"lastPriceTicks": "75877570000000",
"lastTradeTsNs": "1793913933075819794",
"change24hBps": -23,
"high24hTicks": "76566370000000",
"low24hTicks": "75675890000000",
"volume24hBaseScaled": "665070087",
"volume24hQuoteScaled": "505746706733",
"volume24hUsdScaled": "504894996912",
"listedTsNs": "1793880813827104000",
"bestBidTicks": "75847220000000",
"bestBidQtyScaled": "89693218",
"bestAskTicks": "75877570000000",
"bestAskQtyScaled": "87114076",
"indexPriceTicks": "75859331815060"
}
]
}Every parameter is listed in the API reference. The points that differ between the two APIs, or are easy to miss:
- REST filters with
symbols, repeated once per Pair; ConnectRPC withsymbolId. An empty filter returns every Pair, including disabled ones, and a filter takes at most 2000 Pairs. REST skips a symbol it cannot use without an error: an unknown symbol, a comma-joined list, or a disabled Pair returns no row. ConnectRPC rejects an unknownsymbolIdwith HTTP 400 and returns disabled Pairs. limitdefaults to50rows per page, with a maximum of2000.orderByisCHANGE_24H_BPS,VOLUME_24H_USD,LAST_PRICE, orDATE_ADDED, andsortisASCorDESC. WithoutorderBy, rows are sorted by 24h USD volume, descending. ConnectRPC uses the prefixed enum names, such asORDER_BY_CHANGE_24H_BPSandSORT_DESC.sparklineIntervalsis1h,24h,1w, or1mo, repeated for several. It defaults to24hwhenincludeSparklinesis set. Sparkline close prices are ordered newest first.
24h change
change24hBps is the change from the 24h open to the last price, in basis points, truncated toward
zero: -19 is -0.19%. It has minute granularity:
- The 24h open is the price at the start of the minute that contains the time 24 hours ago.
- If the Pair first traded later than that, for example because it was listed less than 24 hours ago, the 24h open is its first trade.
- A Pair with no trade reports
0, and itslastTradeTsNsis0too; ConnectRPC JSON omits both. A Pair that has just started trading reports0until its first minute of trading has ended.
To tell "no trade yet" from "no move", check lastTradeTsNs. Sorting by CHANGE_24H_BPS uses the
absolute value of the change.
Volume and prices
volume24hBaseandvolume24hQuoteare rolling 24h volumes. On ConnectRPC, base volume uses the base Asset'smarketDataVolumeScaleand quote volume the Pair'squoteQuantityScale; both are omitted if the value exceeds the signed 64-bit range.volume24hUsdapplies the current quote-to-USD conversion to the quote volume; on ConnectRPC it is scaled by 1e6. USDT and USDC count at USD parity when no conversion is available. It is omitted for other quote Assets without a current conversion, and those Pairs sort last by USD volume.- REST sends
"0"forlastPrice,high24h,low24h, and the best bid and ask of a Pair without them; ConnectRPC JSON omits them. indexPriceis the current multi-venue index price; ConnectRPC reports0when no fresh index price is available.- Prices on ConnectRPC use the fixed scale of 9; best bid and ask quantities use the Pair's
baseQuantityScale.
Overview updates
public:spot:market_overview:updates:proto is one channel for every Pair. It publishes at a fixed
cadence, and each MarketOverviewBatch holds the complete current row of each Pair that changed
since the previous batch, plus the batch time tsNs. Pairs that did not change are not included.
Replace your row for each symbolId in the batch. Sparklines are not streamed; read them with ListMarketOverview.
Rate limits and SDKs
Quotas for reads and realtime subscriptions are not published yet; the published quotas are in Rate limits. For payload-specific guidance, see WebSocket JSON and WebSocket Protobuf. Every message type is in the API reference.