Polymarket WebSocket Tutorial: Real-Time Data Streaming Guide
Real-time data is the foundation of successful trading bots and analytics tools on Polymarket. While REST APIs are great for fetching data on demand, WebSocket connections push updates instantly—giving you a critical edge when milliseconds matter. This comprehensive tutorial covers everything you need to stream real-time Polymarket data using both the CLOB WebSocket and RTDS (Real-Time Data Socket).
Understanding Polymarket's WebSocket Architecture
Polymarket provides two distinct WebSocket services, each optimized for different use cases:
CLOB WebSocket vs RTDS
| Feature | CLOB WebSocket | RTDS |
|---|---|---|
| Endpoint | wss://ws-subscriptions-clob.polymarket.com | wss://ws-live-data.polymarket.com |
| Best For | Order book streaming, trading, user orders | Broad market data, prices, activity feeds |
| Channels | market, user | Topic-based (activity, prices, clob_market, etc.) |
| Max Instruments | 500 per connection | Not specified |
| Unsubscribe Support | Not supported | Fully supported |
Use CLOB WebSocket when building trading bots that need order book data and trade execution notifications. Use RTDS for dashboards, analytics tools, and monitoring broader market activity.
CLOB WebSocket: Real-Time Order Book Data
The CLOB WebSocket provides level 2 order book data and is essential for building competitive trading bots.
Connection URLs
- Market Channel: wss://ws-subscriptions-clob.polymarket.com/ws/market
- User Channel: wss://ws-subscriptions-clob.polymarket.com/ws/user
Python: Connect to Market Channel
Here's a complete Python example for streaming order book updates:
from websocket import WebSocketApp
import json
import threading
import time
class PolymarketMarketWS:
def __init__(self, asset_ids):
self.asset_ids = asset_ids
self.url = "wss://ws-subscriptions-clob.polymarket.com"
self.ws = None
def on_open(self, ws):
print("Connected to Polymarket CLOB WebSocket")
# Subscribe to market channel
subscription = {
"assets_ids": self.asset_ids,
"type": "market"
}
ws.send(json.dumps(subscription))
self.start_ping_thread()
def on_message(self, ws, message):
data = json.loads(message)
event_type = data.get("event_type")
if event_type == "book":
print(f"Order Book Update:")
print(f" Best Bid: {data.get('bids', [[]])[0]}")
print(f" Best Ask: {data.get('asks', [[]])[0]}")
elif event_type == "price_change":
for change in data.get("price_changes", []):
print(f"Price: Bid {change.get('best_bid')} / Ask {change.get('best_ask')}")
elif event_type == "last_trade_price":
print(f"Trade: {data.get('price')} @ {data.get('size')} ({data.get('side')})")
def on_error(self, ws, error):
print(f"Error: {error}")
def on_close(self, ws, close_status_code, close_msg):
print("WebSocket connection closed")
def start_ping_thread(self):
def send_ping():
while self.ws and self.ws.sock and self.ws.sock.connected:
time.sleep(10)
try:
self.ws.send("PING")
except:
break
ping_thread = threading.Thread(target=send_ping)
ping_thread.daemon = True
ping_thread.start()
def connect(self):
self.ws = WebSocketApp(
self.url + "/ws/market",
on_open=self.on_open,
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close
)
self.ws.run_forever()
# Usage - replace with actual token ID
asset_ids = ["21742633143463906290569050155826241533067272736897614950488156847949938836455"]
market_ws = PolymarketMarketWS(asset_ids)
market_ws.connect()Installation
Install required packages: pip install websocket-client py-clob-client
Message Types Explained
1. Book Message (Full Order Book Snapshot)
{
"event_type": "book",
"asset_id": "21742633...",
"market": "0x...",
"timestamp": 1234567890000,
"bids": [
{"price": "0.52", "size": "100"},
{"price": "0.51", "size": "250"}
],
"asks": [
{"price": "0.53", "size": "150"},
{"price": "0.54", "size": "200"}
]
}2. Price Change Message (Incremental Updates)
{
"event_type": "price_change",
"market": "0x...",
"timestamp": 1234567890000,
"price_changes": [
{
"asset_id": "...",
"best_bid": "0.52",
"best_ask": "0.53",
"changes": [
{"price": "0.52", "side": "BUY", "size": "100"}
]
}
]
}3. Last Trade Price Message
{
"event_type": "last_trade_price",
"asset_id": "...",
"market": "0x...",
"price": "0.52",
"size": "100",
"side": "BUY",
"timestamp": 1234567890000
}See What Whales Are Trading Right Now
Get instant alerts when top traders make moves. Track P&L, win rates, and copy winning strategies.

Free forever. No credit card required.
Authenticated User Channel
The user channel provides real-time updates on your orders and trades. This requires API authentication.
Getting API Credentials
from py_clob_client.client import ClobClient
import os
host = "https://clob.polymarket.com"
private_key = os.getenv("PRIVATE_KEY") # Your wallet private key
client = ClobClient(host, key=private_key, chain_id=137)
api_creds = client.create_or_derive_api_creds()
print(f"API Key: {api_creds.api_key}")
print(f"Secret: {api_creds.api_secret}")
print(f"Passphrase: {api_creds.api_passphrase}")Subscribing to User Channel
from websocket import WebSocketApp
import json
# Use credentials from above
auth = {
"apiKey": api_creds.api_key,
"secret": api_creds.api_secret,
"passphrase": api_creds.api_passphrase
}
def on_open(ws):
subscription = {
"markets": ["0xbd31dc8a20211944f6b70f31557f1001557b59905b7738480ca09bd4532f84af"],
"type": "user",
"auth": auth
}
ws.send(json.dumps(subscription))
def on_message(ws, message):
data = json.loads(message)
event_type = data.get("event_type")
if event_type == "order":
print(f"Order Update: {data.get('order_id')} - Status: {data.get('status')}")
elif event_type == "trade":
print(f"Trade Executed: {data.get('price')} x {data.get('size')}")
ws = WebSocketApp(
"wss://ws-subscriptions-clob.polymarket.com/ws/user",
on_open=on_open,
on_message=on_message
)
ws.run_forever()RTDS: Real-Time Data Socket
RTDS is better suited for building dashboards and analytics tools that need broader market data beyond just order books.
Available RTDS Topics
| Topic | Types | Auth Required |
|---|---|---|
| activity | trades, orders_matched | No |
| crypto_prices | update (BTC, ETH, SOL, DOGE, XRP) | No |
| equity_prices | update (AAPL, TSLA, MSFT, etc.) | No |
| clob_market | agg_orderbook, price_change, last_trade_price | No |
| clob_user | order, trade | Yes |
| comments | comment_created, reaction_created | No |
TypeScript: Using the Official RTDS Client
import { RealTimeDataClient, Message } from "@polymarket/real-time-data-client";
// npm install @polymarket/real-time-data-client
const onMessage = (message: Message): void => {
console.log(`[${message.topic}:${message.type}]`, message.payload);
};
const onConnect = (client: RealTimeDataClient): void => {
console.log("Connected to RTDS");
// Subscribe to market trades
client.subscribe({
subscriptions: [
{
topic: "activity",
type: "trades",
filters: `{"market_slug":"will-bitcoin-hit-100k"}`,
},
],
});
// Subscribe to crypto prices
client.subscribe({
subscriptions: [
{
topic: "crypto_prices",
type: "update",
},
],
});
};
const rtdsClient = new RealTimeDataClient({
onMessage,
onConnect,
});
rtdsClient.connect();JavaScript: Simple Node.js Implementation
const WebSocket = require('ws');
const ws = new WebSocket('wss://ws-live-data.polymarket.com');
ws.on('open', () => {
console.log('Connected to RTDS');
// Subscribe to activity feed
ws.send(JSON.stringify({
action: "subscribe",
subscriptions: [
{
topic: "activity",
type: "trades"
},
{
topic: "crypto_prices",
type: "update"
}
]
}));
// Send ping every 5 seconds
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.ping();
}
}, 5000);
});
ws.on('message', (data) => {
const message = JSON.parse(data);
console.log(`[${message.topic}] ${message.type}:`, message.payload);
});
ws.on('error', console.error);Critical: Heartbeat and Reconnection
WebSocket connections will drop without proper heartbeat handling. This is essential for production systems.
Heartbeat Requirements
- CLOB WebSocket: Send "PING" every 10 seconds
- RTDS: Send PING frame every 5 seconds
Production-Ready Reconnection Pattern
import time
import random
class ReconnectingWebSocket:
def __init__(self, url, on_message, max_retries=5):
self.url = url
self.on_message = on_message
self.max_retries = max_retries
self.ws = None
self.subscriptions = []
def connect_with_backoff(self):
base_delay = 1
max_delay = 60
for attempt in range(self.max_retries):
try:
self._connect()
self._resubscribe()
return True
except Exception as e:
if attempt == self.max_retries - 1:
raise
# Exponential backoff with jitter
delay = min(base_delay * (2 ** attempt), max_delay)
jitter = random.uniform(0, delay * 0.1)
sleep_time = delay + jitter
print(f"Reconnect attempt {attempt + 1} failed. Retrying in {sleep_time:.2f}s")
time.sleep(sleep_time)
return False
def _connect(self):
# Implement actual connection logic
pass
def _resubscribe(self):
# Re-send all subscription messages after reconnect
for subscription in self.subscriptions:
self.ws.send(json.dumps(subscription))Important: Schema Change Alert
The price_change message format was updated on September 15, 2025. The new format includes price_changes array with best_bid and best_ask fields per asset. Update your parsers accordingly.
Real-World Use Cases
1. Real-Time Price Dashboard
class PriceDashboard:
def __init__(self):
self.prices = {}
def on_price_change(self, message):
for change in message.get("price_changes", []):
asset_id = change["asset_id"]
best_bid = float(change["best_bid"])
best_ask = float(change["best_ask"])
mid_price = (best_bid + best_ask) / 2
self.prices[asset_id] = {
"bid": best_bid,
"ask": best_ask,
"mid": mid_price,
"spread": best_ask - best_bid,
"updated": time.time()
}
# Trigger UI update
self.update_display(asset_id)2. Whale Trade Alerts
WHALE_THRESHOLD = 10000 # $10,000 minimum
def on_trade(message):
if message.get("event_type") == "last_trade_price":
price = float(message["price"])
size = float(message["size"])
trade_value = price * size
if trade_value >= WHALE_THRESHOLD:
send_alert({
"type": "WHALE_TRADE",
"asset": message["asset_id"],
"side": message["side"],
"price": price,
"size": size,
"value": trade_value,
"timestamp": message["timestamp"]
})3. Order Book Depth Analysis
def analyze_orderbook(book_event):
bids = book_event.get("bids", [])
asks = book_event.get("asks", [])
if not bids or not asks:
return None
# Calculate spread
best_bid = float(bids[0]["price"])
best_ask = float(asks[0]["price"])
spread = best_ask - best_bid
# Calculate depth at top 5 levels
bid_depth = sum(float(b["size"]) for b in bids[:5])
ask_depth = sum(float(a["size"]) for a in asks[:5])
# Detect imbalance (useful for trading signals)
total_depth = bid_depth + ask_depth
imbalance = bid_depth / total_depth if total_depth > 0 else 0.5
return {
"spread": spread,
"spread_pct": (spread / best_bid) * 100,
"bid_depth": bid_depth,
"ask_depth": ask_depth,
"imbalance": imbalance, # >0.5 = more buying pressure
"signal": "bullish" if imbalance > 0.6 else "bearish" if imbalance < 0.4 else "neutral"
}Performance Optimization
Handle High Message Volume
import asyncio
from collections import deque
class MessageProcessor:
def __init__(self):
self.queue = deque(maxlen=10000)
self.running = True
async def process_loop(self):
while self.running:
batch = []
# Collect messages for 100ms
deadline = time.time() + 0.1
while time.time() < deadline and self.queue:
batch.append(self.queue.popleft())
if batch:
await self.process_batch(batch)
else:
await asyncio.sleep(0.01)
async def process_batch(self, messages):
# Group by type for efficient processing
by_type = {}
for msg in messages:
event_type = msg.get("event_type", "unknown")
if event_type not in by_type:
by_type[event_type] = []
by_type[event_type].append(msg)
# Process each type
for event_type, msgs in by_type.items():
await self.handle_type(event_type, msgs)
def on_message(self, message):
# Quick queue, don't block WebSocket thread
self.queue.append(message)Memory-Efficient Data Storage
from collections import deque
class MarketDataBuffer:
def __init__(self, max_trades=1000, max_books=100):
self.trades = deque(maxlen=max_trades)
self.orderbooks = {} # Only keep latest per asset
def add_trade(self, trade):
self.trades.append({
"price": trade["price"],
"size": trade["size"],
"side": trade["side"],
"ts": trade["timestamp"]
})
def update_orderbook(self, asset_id, book):
# Only store top 10 levels to save memory
self.orderbooks[asset_id] = {
"bids": book["bids"][:10],
"asks": book["asks"][:10],
"ts": book["timestamp"]
}Common Issues and Solutions
Connection Drops
- Cause: Missing heartbeat, network issues, server maintenance
- Solution: Implement ping/pong, exponential backoff reconnection, re-subscribe after reconnect
Missing Messages
- Cause: Gap in message sequence during reconnection
- Solution: Request full order book snapshot after reconnect, track message sequences
Rate Limiting
- Cause: Too many subscriptions or connections
- Solution: Max 500 instruments per CLOB connection, use connection pooling for more
Libraries and Resources
Official Libraries
- Python:
py-clob-client- pip install py-clob-client - TypeScript:
@polymarket/real-time-data-client- npm install @polymarket/real-time-data-client - TypeScript:
@polymarket/clob-client- npm install @polymarket/clob-client
Community Libraries
- TypeScript:
@nevuamarkets/poly-websockets- Auto-reconnection, rate limiting - Rust:
polymarket-rtds- High-performance Rust client - Go:
go-polymarket-real-time-data-client
Documentation Links
- CLOB WebSocket Overview: docs.polymarket.com/developers/CLOB/websocket/wss-overview
- RTDS Overview: docs.polymarket.com/developers/RTDS/RTDS-overview
- Market Channel: docs.polymarket.com/developers/CLOB/websocket/market-channel
- User Channel: docs.polymarket.com/developers/CLOB/websocket/user-channel
- Authentication: docs.polymarket.com/developers/CLOB/authentication
Build Faster with PolyTrack
PolyTrack uses these exact WebSocket techniques to deliver real-time whale alerts, price notifications, and portfolio tracking. Start your free trial and see how professional traders monitor Polymarket.
Next Steps
Now that you understand WebSocket streaming, you're ready to build production applications:
- Build a Polymarket Trading Bot - Complete guide to automated trading
- Polymarket API Guide - REST API endpoints and authentication
- Track Smart Money - Use trade data to follow whale wallets
- Portfolio Tracker - Build real-time position monitoring
Frequently Asked Questions
Polymarket offers two WebSocket services: CLOB WebSocket (wss://ws-subscriptions-clob.polymarket.com) for order book and trading data, and RTDS (wss://ws-live-data.polymarket.com) for broader market data, prices, and activity feeds.
Related Articles
Stop Guessing. Start Following Smart Money.
Get instant alerts when whales make $10K+ trades. Track P&L, win rates, and copy winning strategies.