API · Reference
Polymarket has two realtime systems, on two different hosts. The CLOB WebSocket streams the order book and your own order updates; the RTDS feed streams live price data (crypto, equities) and comments. Mixing the two up is the most common mistake. This page keeps them straight, with the exact endpoints, the heartbeat, and a correct reconnect.
- CLOB:
wss://ws-subscriptions-clob.polymarket.com/ws/· channelsmarket(public, order book),user(auth, your orders/fills),sports(live game state for sports markets), and anrfqgateway (live Request-for-Quote state - see the RFQ section). - RTDS:
wss://ws-live-data.polymarket.com· topicscrypto_prices,crypto_prices_chainlink,equity_prices,comments.
CLOB market channel (no auth)
Subscribe to one or more token IDs and receive the order book and updates: an initial
book snapshot, then price_change, tick_size_change and
last_trade_price events. No credentials needed.
import asyncio, json, websockets
async def stream(token_ids):
url = "wss://ws-subscriptions-clob.polymarket.com/ws/market"
async with websockets.connect(url, ping_interval=10) as ws:
await ws.send(json.dumps({"assets_ids": token_ids, "type": "market"}))
async for msg in ws:
evt = json.loads(msg)
print(evt.get("event_type"), evt)
asyncio.run(stream([TOKEN_ID]))
import WebSocket from "ws";
const ws = new WebSocket("wss://ws-subscriptions-clob.polymarket.com/ws/market");
ws.on("open", () => ws.send(JSON.stringify({ assets_ids: [TOKEN_ID], type: "market" })));
ws.on("message", (d) => console.log(JSON.parse(d)));
CLOB user channel (auth)
The user channel reports your order lifecycle (placements, matches, cancels). It
needs your API key/secret/passphrase and you subscribe by condition_id (market), not token
ID. Connect to wss://ws-subscriptions-clob.polymarket.com/ws/user and include your auth in
the subscribe payload:
await ws.send(json.dumps({
"type": "user",
"markets": [CONDITION_ID], # subscribe by condition_id, not token_id
"auth": {"apiKey": API_KEY, "secret": SECRET, "passphrase": PASSPHRASE},
}))
# events: "order" (placed/cancelled) and "trade" (your fills)
RTDS price feeds
A separate host, wss://ws-live-data.polymarket.com, for live price data,
not order books. Subscribe to a topic: crypto_prices (Binance), crypto_prices_chainlink
(Chainlink oracle), equity_prices, or comments. Useful for the underlying signal in
crypto/equity prediction-market bots.
Heartbeat & reconnect
- Heartbeat: send a
PINGroughly every 10 seconds; the server repliesPONG. Miss it and the connection drops. - Modify live: you can add/remove topics or assets on an open connection; you don't need to reconnect to change your subscription.
- Reconnect correctly: after any drop, re-fetch the REST order-book snapshot
(
GET /book) before trusting incremental updates again. Streamed deltas you missed while disconnected are gone; the REST snapshot re-syncs your local book.
The whole pattern in one resilient loop - heartbeat, reconnect with backoff, and a REST resync on every (re)connect:
import asyncio, json, time, requests, websockets
async def resilient_stream(token_id, on_event):
url = "wss://ws-subscriptions-clob.polymarket.com/ws/market"
backoff = 1
while True:
try:
async with websockets.connect(url, ping_interval=10) as ws:
# 1) REST snapshot first so the local book is correct before deltas
book = requests.get("https://clob.polymarket.com/book",
params={"token_id": token_id}).json()
on_event({"event_type": "snapshot", "book": book})
# 2) subscribe and stream
await ws.send(json.dumps({"assets_ids": [token_id], "type": "market"}))
backoff = 1 # reset after a clean connect
async for msg in ws:
on_event(json.loads(msg))
except Exception as e:
print("ws drop, resync in", backoff, "s:", e)
await asyncio.sleep(backoff)
backoff = min(backoff * 2, 30) # exponential backoff, capped
ws-subscriptions-clob); crypto/equity prices and comments come from RTDS
(ws-live-data). They are different services with different payloads.Next: CLOB REST reference · Rate limits & backoff · Build: stream the book in a bot.



