Realtime uses the binary Centrifugo Protobuf WebSocket protocol, not ConnectRPC streaming and
not SBE. Service subscribe* helpers return an AsyncSubscription you consume with async for / async with.
The SDK negotiates the centrifuge-protobuf WebSocket subprotocol. Both Centrifugo control frames
and :proto channel publications are binary Protobuf. ConnectRPC's optional JSON wire mode is
unrelated and never changes realtime framing.
Managed order-book and market-overview create methods return only after the WebSocket handshake
and initial snapshot succeed. Pass on_error= so a terminal snapshot, decode, transport, or
buffering failure is observable immediately; callback exceptions are isolated from the worker.
Contract
sub = await client.orders.subscribe(account_id=account_id)
sub.set_on_error(lambda error: print(f"realtime interruption: {error}"))
async with sub:
async for order in sub:
print(order.status, order.order_id)- Subscribe awaits handshake (including private token fetch) before returning.
- Initial auth failures raise; they do not spin forever in the background.
- On overflow,
PolyesterRealtimeOverflowErrorfaults the subscription (fail-closed). - Reconnect uses capped exponential backoff with per-subscription jitter.
What you can stream
| Stream | Method | Auth |
|---|---|---|
| Public trades | client.market_data.subscribe_trades | public |
| Candles | client.market_data.subscribe_candles | public |
| Order book | client.orderbook managed subscription | public |
| Market overview | client.market_overview.subscribe* | public |
| Heatmap | client.heatmap.subscribe_live | public |
| Lifecycle | client.lifecycle.subscribe_open_flows / subscribe_flow_detail | public or private |
| Zipped-asset supply | client.zipper.subscribe_zipped_asset_supply | public |
| Profile identity | client.auth.profile.subscribe_identity | public |
| Your orders | client.orders.subscribe | private |
| Your triggers | client.triggers.subscribe / subscribe_events | private |
| Your trades | client.trades.subscribe | private |
| Balances | client.balances.subscribe | private |
| Transfers | client.transfers.subscribe | private |
| API keys / subaccounts | matching subscribe helpers | private |
| API/subaccount policies | client.policies.subscribe_* | private |
| Address-book invalidations | client.address_book.subscribe_view_invalidations | private |
Private streams need API-key auth and Account ID.
PolyesterAuthError is non-transient; inspect its code, context, and endpoint, then
update the key policy before reconnecting.Public trades example
subscription = await client.market_data.subscribe_trades(symbol="BTC-USDT")
async with subscription:
async for trade in subscription:
print(trade.price)
breakSnapshot-then-stream
Managed order-book subscriptions snapshot via REST, then stream deltas. Sequence gaps trigger a REST snapshot refresh. Prefer these helpers over wiring raw channels yourself.
Market overview uses the same managed snapshot-then-stream pattern. For entity streams (orders, balances, trades, transfers, triggers), the event channel is not a complete starting snapshot: subscribe first, fetch the corresponding list/snapshot, buffer events that arrive during the read, then reconcile by entity ID plus version/timestamp. After reconnect, re-read authoritative state before declaring the bot healthy.
Typed subscriptions do not recover missed publications across reconnect. Poll sub.take_resubscribed() (or watch sub.resubscribes) after each receive and treat a latch as a
gap: rebuild from REST before trusting the stream again. A WebSocket read timeout is treated as
connection death so half-open sockets reconnect instead of freezing.
Do not place or maintain quotes from a raw order-book delta stream. Use the managed order-book helper, which detects sequence gaps and refreshes state.
Overflow
PolyesterRealtimeOverflowError and closes the
subscription. It does not silently drop updates. Fix consumer speed, then
resubscribe and rebuild authoritative state. A new subscription alone does not recover missed
entity events.Connection notes
client.realtime is always present. You rarely call it directly. Hyphenated private channel names
(for example segments containing api-keys) are valid for API-key signing.
Session model: WebSocket session model. Reference: Realtime.