WebSocket

February 19, 2026 · View on GitHub

Three channels for real-time data. Market and sports channels are public; user channel requires API credentials.

Channels

ChannelEndpointAuth
Marketwss://ws-subscriptions-clob.polymarket.com/ws/marketNo
Userwss://ws-subscriptions-clob.polymarket.com/ws/userYes
Sportswss://sports-api.polymarket.com/wsNo

Market Channel

Public. Subscribes by asset IDs (token IDs).

Subscribe

{
  "assets_ids": ["TOKEN_ID_1", "TOKEN_ID_2"],
  "type": "market",
  "custom_feature_enabled": true
}

Set custom_feature_enabled: true to enable best_bid_ask, new_market, and market_resolved events.

Event Types

EventTriggerKey Fields
bookOn subscribe + when trade affects bookbids[], asks[], hash, timestamp
price_changeOrder placed or cancelledprice_changes[] with price, size, side, best_bid, best_ask
last_trade_priceTrade executedprice, side, size, fee_rate_bps
tick_size_changePrice hits >0.96 or <0.04old_tick_size, new_tick_size
best_bid_askTop-of-book changesbest_bid, best_ask, spread
new_marketMarket createdquestion, assets_ids, outcomes
market_resolvedMarket resolvedwinning_asset_id, winning_outcome

Events requiring custom_feature_enabled: true: best_bid_ask, new_market, market_resolved.

tick_size_change is critical for bots — if tick size changes and you use the old one, orders are rejected.

A price_change with size: "0" means the price level was removed from the book.

Example Messages

// book
{
  "event_type": "book",
  "asset_id": "TOKEN_ID",
  "market": "0xCONDITION_ID",
  "bids": [{"price": ".48", "size": "30"}],
  "asks": [{"price": ".52", "size": "25"}],
  "timestamp": "123456789000",
  "hash": "0x..."
}
// price_change
{
  "event_type": "price_change",
  "market": "0xCONDITION_ID",
  "price_changes": [{
    "asset_id": "TOKEN_ID",
    "price": "0.5",
    "size": "200",
    "side": "BUY",
    "hash": "...",
    "best_bid": "0.5",
    "best_ask": "1"
  }],
  "timestamp": "..."
}

User Channel

Authenticated. Subscribes by condition IDs (market IDs), not asset IDs. The markets field is optional — omit it to receive events for all markets.

Subscribe

{
  "auth": {
    "apiKey": "your-api-key",
    "secret": "your-api-secret",
    "passphrase": "your-passphrase"
  },
  "markets": ["0xCONDITION_ID"],
  "type": "user"
}

Event Types

EventTrigger
tradeTrade lifecycle: MATCHED, MINED, CONFIRMED, RETRYING, FAILED
orderOrder lifecycle: PLACEMENT, UPDATE, CANCELLATION

Trade Message

{
  "event_type": "trade",
  "id": "trade-uuid",
  "market": "0xCONDITION_ID",
  "asset_id": "TOKEN_ID",
  "side": "BUY",
  "size": "10",
  "price": "0.57",
  "status": "MATCHED",
  "maker_orders": [{ "order_id": "0x...", "matched_amount": "10", "price": "0.57" }],
  "type": "TRADE"
}

Order Message

{
  "event_type": "order",
  "id": "0xORDER_ID",
  "market": "0xCONDITION_ID",
  "asset_id": "TOKEN_ID",
  "side": "SELL",
  "price": "0.57",
  "original_size": "10",
  "size_matched": "0",
  "type": "PLACEMENT"
}

Order types: PLACEMENT, UPDATE (partial fill), CANCELLATION.

Sports Channel

No subscription message needed. Connect and receive all active sports data.

// sport_result
{ "type": "sport_result", ... }  // Live scores, periods, status

Dynamic Subscribe / Unsubscribe

Modify subscriptions without reconnecting:

// Market channel — subscribe to more
{ "assets_ids": ["NEW_TOKEN_ID"], "operation": "subscribe", "custom_feature_enabled": true }

// Market channel — unsubscribe
{ "assets_ids": ["OLD_TOKEN_ID"], "operation": "unsubscribe" }

// User channel — subscribe to more markets
{ "markets": ["0xNEW_CONDITION_ID"], "operation": "subscribe" }

Heartbeat

Market & User Channels

Send PING every 10 seconds. Server responds with PONG.

const ws = new WebSocket("wss://ws-subscriptions-clob.polymarket.com/ws/market");

ws.onopen = () => {
  // Subscribe...
  setInterval(() => ws.send("PING"), 10_000);
};

ws.onmessage = (event) => {
  if (event.data === "PONG") return;
  const msg = JSON.parse(event.data);
  // handle msg.event_type
};

Sports Channel

Server sends ping every 5 seconds. Respond with pong within 10 seconds or connection closes.

Full TypeScript Example

const ws = new WebSocket("wss://ws-subscriptions-clob.polymarket.com/ws/market");

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: "market",
    assets_ids: ["TOKEN_ID"],
    custom_feature_enabled: true,
  }));
  setInterval(() => ws.send("PING"), 10_000);
};

ws.onmessage = (event) => {
  if (event.data === "PONG") return;
  const msg = JSON.parse(event.data);
  switch (msg.event_type) {
    case "book":
      console.log("Book snapshot:", msg.bids.length, "bids", msg.asks.length, "asks");
      break;
    case "price_change":
      for (const pc of msg.price_changes) {
        console.log(`${pc.side} ${pc.size}@${pc.price} (best: ${pc.best_bid}/${pc.best_ask})`);
      }
      break;
    case "last_trade_price":
      console.log(`Trade: ${msg.side} ${msg.size}@${msg.price}`);
      break;
    case "tick_size_change":
      console.log(`Tick: ${msg.old_tick_size} → ${msg.new_tick_size}`);
      break;
  }
};

Troubleshooting

  • Connection closes immediately: send subscription message right after open
  • Drops after ~10s: you're not sending PING heartbeats
  • No messages: verify asset IDs are correct and markets are active
  • Auth failed (user channel): check API credentials haven't expired