Polymarket Replaced RTDS: Migrating to PolyBolt
On 15 September Polymarket quietly made RTDS legacy for reference prices. If you stream spot into a bot, six things changed at once — including the price source and the symbol format — and most of them fail silently rather than loudly.
What changed, and when
On 15 September 2026 Polymarket shipped PolyBolt at wss://ws-live-v2.polymarket.com/ws and made RTDS legacy for reference prices. crypto_prices, crypto_prices_chainlink and equity_prices all map to new channels. The comments topic stays on RTDS for now.
If your bot streams spot prices to decide anything — and on crypto Up/Down markets, most do — this touches you. Six things changed at once, and the awkward part is that several of them fail quietly: you stay connected, you just stop getting the prices you think you are getting.
The six breaking changes
| RTDS (legacy) | PolyBolt | |
|---|---|---|
| URL | wss://ws-live-data.polymarket.com | wss://ws-live-v2.polymarket.com/ws |
| Auth | Public | CLOB API credentials required |
| Heartbeat | Client sends PING every 5s | Nothing to send — server pings every 25s |
| Subscribe frame | {action, subscriptions:[{topic,type,filters}]} | {op, subscriptions:[{channel,filter}]} — filter is an object, not a string |
| Crypto source | Binance / Chainlink | Pyth |
| Symbols | btcusdt, btc/usd | btcusd — lowercase, USD not USDT |
The two that will cost you the most time
The price source changed to Pyth. Your reference price is now coming from a different oracle than it was on 14 September. If you calibrated thresholds against Binance ticks, recalibrate. And the symbol format changed — btcusdt and btc/usd are both wrong now. A bad symbol does not throw; you get a subscription that never delivers.
Channel mapping
| RTDS topic | PolyBolt channel | Filter | Note |
|---|---|---|---|
crypto_prices | price.crypto | {"symbol":"btcusd"} | Source Binance → Pyth; USDT → USD |
crypto_prices_chainlink | price.crypto | {"symbol":"btcusd"} | Source Chainlink → Pyth |
crypto_prices_twap_sixty | price.crypto.twap | {"symbol":"btcusd","window_seconds":60} | 60s Chainlink TWAP |
crypto_prices_twap_thirty | None | — | No replacement exists |
equity_prices | price.equity | {"symbol":"aapl"} | Same catalog, lowercase |
The 30-second TWAP is simply gone
crypto_prices_twap_thirty has no PolyBolt equivalent. That matters more than it looks: 5-minute crypto markets briefly resolved on a 30-second TWAP after 7 August, then moved to 60 seconds on 14 August. If anything in your stack still consumes the 30s window, it has no migration path — you need the 60s channel and a recalibrated model.
Connecting
Authenticate on the open connection first, wait for confirmation, then subscribe. Reference-price channels reject unauthenticated subscriptions.
{
"op": "auth",
"rid": "a1",
"auth": {
"apiKey": "<api key>",
"secret": "<api secret>",
"passphrase": "<api passphrase>"
}
}Those same credentials are the ones you derive for CLOB trading — key, secret and passphrase together. If you have never created them, the endpoint guide covers the L1/L2 flow.
Three things PolyBolt gives you that RTDS did not
- A snapshot on every subscribe. Each subscription returns the preceding two minutes of prices with
snapshot: true, possibly empty. Seed local state from it, then apply live updates. Under RTDS a quiet symbol just gave you silence and you had no idea whether you were connected or simply between ticks. - A dense
seqand adroppedcounter. You can finally detect gaps. RTDS had no sequence at all, so a missed message was invisible. Trackseqper channel and treat any jump as a resync trigger. - Typed close codes instead of raw
1006:4001auth,4002slow consumer,4003draining,4008policy. A4002means you are too slow to drain the socket — that is a consumer bug, not a network problem, and reconnecting will not fix it.
Use full_accuracy_value, not value
value is a float; full_accuracy_value is an exact decimal string. On a market that settles on a TWAP comparison, float rounding at the eighth decimal is not something you want deciding a trade. Also: legacy TWAP frames used window_s and an E18 fixed-point price. PolyBolt sends a decimal string — do not apply the E18 conversion, or your prices will be off by eighteen orders of magnitude.
Connection limits
RTDS published none. PolyBolt does, and breaching any of them closes the socket with 4008:
| Limit | Value |
|---|---|
| Subscriptions per connection | 64 |
| Subscribe frames per second | 20 |
| Maximum frame size | 64 KB |
| Auth frames | 8 |
64 subscriptions is the one to plan around. If you track BTC, ETH and SOL across spot and TWAP that is six, so there is plenty of headroom for a crypto bot — but a system that fans out across the equity catalogue will hit it.
Migrating a bot, in order
- Derive CLOB API credentials if you do not have them. RTDS was public; PolyBolt is not. This is the step that blocks everything else.
- Change the URL to
wss://ws-live-v2.polymarket.com/ws. - Delete your client heartbeat. The 5-second
PINGloop is not just unnecessary, it is now noise. The server pings you every 25 seconds. - Rewrite subscribe frames —
action→op,topic→channel, andfilterbecomes a JSON object rather than a string. - Fix every symbol:
btcusdtandbtc/usdboth becomebtcusd. Lowercase, USD. - Handle the snapshot frame so you do not treat two minutes of backfill as live ticks.
- Add
seqgap detection and resync on a jump. - Recalibrate anything tuned to Binance prices. The source is Pyth now.
If you use the TypeScript SDK
@polymarket/client 0.11.0 and later wraps this: client.subscribe([{ topic: "prices.crypto", symbols: ["btcusd"] }]) on an authenticated SecureClient, and you iterate typed events with subscribe and update types. That handles auth, framing and heartbeats for you — see the SDK guide for the wider migration picture.
What this does not change
Worth being precise, because it is easy to over-migrate. The CLOB market and user channels are untouched. wss://ws-subscriptions-clob.polymarket.com/ws/market still carries order book and price updates, still wants a client PING every 10 seconds, and still hides best_bid_ask, new_market and market_resolved behind custom_feature_enabled. Only reference prices moved. Details in the WebSocket guide.
And the deeper limitation is unchanged too: a stream gives you prices from the moment you connect. It cannot tell you what the book looked like last Tuesday. Polymarket returns 404 for a resolved market's book and keeps only about one point a minute of price history, which is why backtesting needs an archive recorded while the markets ran. See how Up/Down markets work or look up a specific market in the market lookup.
Frequently asked questions
- What is PolyBolt and when did it replace RTDS?
- PolyBolt is Polymarket's WebSocket for crypto, equity and TWAP reference prices, launched 15 September 2026 at wss://ws-live-v2.polymarket.com/ws. It makes RTDS legacy for reference prices: crypto_prices, crypto_prices_chainlink and equity_prices all map to new PolyBolt channels. The comments topic remains on RTDS for now.
- Do I need authentication for PolyBolt?
- Yes. Reference-price channels require CLOB API credentials — key, secret and passphrase — sent as an auth frame on the open connection before subscribing. This is the main operational difference from RTDS, which was public. Wait for the server to confirm authentication before sending subscribe frames.
- What replaced crypto_prices_chainlink?
- The price.crypto channel with filter {"symbol":"btcusd"}. Two things change beyond the name: the source moves from Chainlink to Pyth, and the symbol format changes from btc/usd to lowercase btcusd. For the 60-second Chainlink TWAP, use price.crypto.twap with window_seconds set to 60. The 30-second TWAP topic has no PolyBolt replacement.
- Does PolyBolt still need a client PING?
- No. Under RTDS the client sent a text PING every 5 seconds. On PolyBolt the server pings every 25 seconds and the client sends nothing, though an optional {"op":"ping"} is accepted. Note this is different from the CLOB market channel, which is unchanged and still requires a client PING every 10 seconds.
- Why am I connected to PolyBolt but receiving no prices?
- Almost always a symbol format problem. PolyBolt uses lowercase symbols ending in usd, so btcusdt and btc/usd are both invalid — use btcusd. A bad symbol does not raise an error; the subscription simply never delivers. Check also that authentication was confirmed before you sent the subscribe frame, and that you are not treating the initial snapshot frame as the only data.
- What do the PolyBolt close codes mean?
- 4001 is an authentication failure, 4002 means you are a slow consumer and not draining the socket fast enough, 4003 means the server is draining, and 4008 is a policy breach such as exceeding 64 subscriptions, 20 subscribe frames per second, a 64 KB frame or 8 auth frames. A 4002 is a bug in your consumer rather than a network fault, so reconnecting without fixing throughput will just repeat it.
Get the historical data Polymarket does not keep
PolyTest records Polymarket crypto Up/Down markets as they run — 240M+ snapshots with 8 levels of order book depth, sub-second timestamps, and resolved markets preserved. Start with a 3-day free trial.
Keep reading
Polymarket WebSockets: Real-Time Data That Does Not Drop
Polling the order book is the most common way to burn a rate limit budget for no benefit. Here are the four streams, the subscription options nobody documents, and the failure mode where your bot receives silence and calls it calm.
How to Build a Polymarket Trading Bot
Most Polymarket bot tutorials are built on SDKs that no longer exist. This is the current path end to end — what to install in 2026, how auth actually works, where the data gaps are, and the failure modes that kill bots in week one.
The Polymarket Python SDK in 2026 (and Migrating off py-clob-client)
If your code still imports ClobClient from py_clob_client, it is built on a superseded package — and so is most of the Polymarket code an AI assistant will write for you. Here is the current SDK and how to move.