Skip to main content
Room ID: polymarket_events_stream
Endpoint: wss://api.struct.to/ws
Rate: 0.025 credits per message
Low-latency push feed of PolymarketEvent rows — same shape that GET /polymarket/events returns. The server maintains an in-memory cache of open events only, refreshed via a slow full poll plus a fast 500ms newest-first poll, and merged live from the prediction_event_metrics and prediction_trades Kafka streams with per-timeframe block/timestamp ordering. No initial snapshot is pushed on subscribe. Clients seed local state from GET /polymarket/events and then apply deltas from this stream.

Subscription model

Each client has up to 8 active slots per room (4 cadences × 2 modes). Re-subscribing to the same (interval_ms, mode) pair replaces the previous subscription. Unsubscribe one slot with action: "unsubscribe" plus interval_ms and mode, or clear everything with action: "unsubscribe_all".
  • Cadence (interval_ms): 500, 1000, 3000, or 10000. Each cadence is a separate flush bucket.
  • Filter mode: evaluated in-memory on every flush against rows that actually changed since the last tick. Same validation as the REST list endpoint.
  • Ids mode: filter by explicit event_slugs and/or event_ids. Max 500 ids per subscription.
Updates fire only when a cache row is dirtied by (a) a fresh Kafka metric snapshot with latest_block >= cached, (b) a confirmed trade with block >= cached, (c) a slow-poll field diff, or (d) the fast newest-first poll discovering a brand-new event. Quiet events produce zero messages.

Subscribe

Message fields

FieldTypeRequiredDescription
action"subscribe" | "unsubscribe" | "unsubscribe_all"YesSlot lifecycle action.
interval_ms500 | 1000 | 3000 | 10000For subscribe / unsubscribeFlush cadence. Defaults to 1000 if omitted.
mode"filter" | "ids"NoSubscription mode. Defaults to filter.
filterEventsStreamFiltermode=filter onlyFilter body; all fields optional.
event_slugsstring[]mode=ids onlyEvent slugs to watch.
event_idsstring[]mode=ids onlyEvent ids to watch.

Filter fields (mode=filter)

search is a case-insensitive substring match on title (3–100 chars). All other fields follow the same validation as the REST list endpoint (timeframe, list size caps).

Example — filter mode

{
  "type": "join_room",
  "payload": {
    "room_id": "polymarket_events_stream"
  }
}
{
  "type": "room_message",
  "payload": {
    "room_id": "polymarket_events_stream",
    "message": {
      "action": "subscribe",
      "interval_ms": 1000,
      "mode": "filter",
      "filter": {
        "search": "election",
        "timeframe": "24h",
        "min_volume": 10000
      }
    }
  }
}

Example — ids mode

{
  "type": "room_message",
  "payload": {
    "room_id": "polymarket_events_stream",
    "message": {
      "action": "subscribe",
      "interval_ms": 500,
      "mode": "ids",
      "event_slugs": ["us-election-2028"],
      "event_ids": ["12345"]
    }
  }
}

Unsubscribe one slot

{
  "type": "room_message",
  "payload": {
    "room_id": "polymarket_events_stream",
    "message": {
      "action": "unsubscribe",
      "interval_ms": 500,
      "mode": "ids"
    }
  }
}

Response

{
  "type": "events_stream_subscribe_response",
  "room_id": "polymarket_events_stream",
  "data": {
    "mode": "ids",
    "interval_ms": 500,
    "event_slugs": ["us-election-2028"],
    "rejected": [],
    "error": null
  }
}

Events

events_stream_update

Server-pushed event fired only for rows that changed AND matched this subscription since the last flush tick. data contains full PolymarketEvent rows — not deltas — so clients should merge by id.

Envelope

FieldTypeDescription
type"events_stream_update"Envelope discriminator.
room_id"polymarket_events_stream"Room identifier.
mode"filter" | "ids"The mode this subscription was created with.
interval_ms500 | 1000 | 3000 | 10000The cadence slot this event is flushed under.
dataPolymarketEvent[]Full event rows, same shape as GET /polymarket/events.

PolymarketEvent

FieldTypeDescription
idstringEvent ID.
event_slugstring | nullURL-safe event slug.
titlestring | nullHuman-readable title.
tickerstring | nullShort ticker, when present.
descriptionstring | nullLong description.
resolution_sourcestring | nullResolution source URL.
categorystring | nullPrimary category label.
image_urlstring | nullCDN image URL.
market_countintegerNumber of child markets.
created_timeinteger | nullUnix seconds.
closed_timeinteger | nullUnix seconds.
start_timeinteger | nullUnix seconds.
end_timeinteger | nullUnix seconds.
neg_riskbooleanWhether this event uses the neg-risk market.
neg_risk_market_idstring | nullNeg-risk market ID, when applicable.
game_statusstring | nullSports-event game status.
show_market_imagesbooleanWhether child market images should be shown.
statusstring | null"open" or "closed".
metricsRecord<Timeframe, SimpleTimeframeMetrics>Keyed by timeframe (1m, 5m, 30m, 1h, 6h, 24h, 7d, 30d).
tagsPolymarketTag[]Event tags.
marketsEventMarket[]Child markets with enriched outcomes.
seriesPolymarketSeries | nullParent series, when present.

SimpleTimeframeMetrics

FieldTypeDescription
volumenumberUSD volume within the window.
feesnumberUSD fees within the window.
txnsintegerTrade count within the window.
unique_tradersintegerUnique wallet count within the window.

PolymarketTag

FieldTypeDescription
idstringTag ID.
labelstringDisplay label.
slugstring | nullURL-safe slug.

EventMarket

FieldTypeDescription
condition_idstring0x-prefixed condition ID.
idstring | nullMarket ID.
titlestring | nullMarket title.
questionstringMarket question.
market_slugstringURL-safe market slug.
statusstringMarket status.
created_timeinteger | nullUnix seconds.
end_timeinteger | nullUnix seconds.
volumenumber | nullLifetime USD volume.
liquidity_usdnumber | nullCurrent USD liquidity.
volume_24hrnumber | null24h USD volume.
image_urlstring | nullCDN image URL.
market_maker_addressstring | nullMarket maker contract.
creatorstring | nullWallet address of creator.
categorystring | nullCategory label.
accepting_ordersboolean | nullWhether CLOB is accepting new orders.
uma_resolution_statusstring | nullUMA oracle resolution status.
clob_rewardsClobReward[]Active reward configs.
outcomesEventMarketOutcome[]Market outcomes.
winning_outcomeEventMarketOutcome | nullResolved winning outcome, when applicable.

EventMarketOutcome

FieldTypeDescription
namestringOutcome label (e.g. "Yes").
pricenumber | nullLatest price (0 – 1).
position_idstring | nullERC-1155 outcome token ID (decimal string).
outcome_indexinteger | null0-indexed outcome position.

PolymarketSeries

FieldTypeDescription
idstringSeries ID.
slugstring | nullURL-safe slug.
tickerstring | nullTicker, when present.
titlestring | nullDisplay title.
descriptionstring | nullLong description.
series_typestring | nullSeries type discriminator.
recurrencestring | nullRecurrence cadence (e.g. "daily").
layoutstring | nullUI layout hint.
image_urlstring | nullCDN image URL.
icon_urlstring | nullCDN icon URL.
activebooleanWhether the series is currently active.
closedbooleanWhether the series is closed.
archivedbooleanWhether archived.
featuredbooleanWhether featured in discovery surfaces.
restrictedbooleanWhether the series is region-restricted.
pyth_token_idstring | nullPyth price feed ID, when applicable.
cg_asset_namestring | nullCoinGecko asset name, when applicable.
start_dateinteger | nullUnix seconds.
event_countintegerNumber of child events.
See the Markets Stream room for the ClobReward shape — it is shared across both stream rooms.

Example

{
  "type": "events_stream_update",
  "room_id": "polymarket_events_stream",
  "mode": "ids",
  "interval_ms": 500,
  "data": [
    {
      "id": "12345",
      "event_slug": "us-election-2028",
      "title": "US Presidential Election 2028",
      "ticker": "PRES28",
      "description": "Who will win the 2028 US presidential election?",
      "resolution_source": "https://example.com/resolution",
      "category": "politics",
      "image_url": "https://cdn.struct.to/events/us-election-2028.png",
      "market_count": 8,
      "created_time": 1743400000,
      "closed_time": null,
      "start_time": 1743500000,
      "end_time": 1893456000,
      "neg_risk": true,
      "neg_risk_market_id": "0xneg...",
      "game_status": null,
      "show_market_images": true,
      "status": "open",
      "metrics": {
        "24h": { "volume": 125000.5, "fees": 250.0, "txns": 340, "unique_traders": 85 },
        "7d": { "volume": 810000.0, "fees": 1620.0, "txns": 2210, "unique_traders": 540 }
      },
      "tags": [{ "id": "42", "label": "Politics", "slug": "politics" }],
      "markets": [
        {
          "condition_id": "0xabc123...",
          "id": "m_1",
          "title": "Candidate A wins",
          "question": "Will Candidate A win the 2028 election?",
          "market_slug": "candidate-a-wins-2028",
          "status": "open",
          "created_time": 1743400000,
          "end_time": 1893456000,
          "volume": 42000.0,
          "liquidity_usd": 15000.0,
          "volume_24hr": 3200.0,
          "image_url": "https://cdn.struct.to/markets/candidate-a.png",
          "market_maker_address": "0xmm...",
          "creator": "0xcreator...",
          "category": "politics",
          "accepting_orders": true,
          "uma_resolution_status": null,
          "clob_rewards": [],
          "outcomes": [
            { "name": "Yes", "price": 0.42, "position_id": "12345678901234567", "outcome_index": 0 },
            { "name": "No",  "price": 0.58, "position_id": "98765432109876543", "outcome_index": 1 }
          ],
          "winning_outcome": null
        }
      ],
      "series": null
    }
  ]
}
Last modified on April 14, 2026