> ## Documentation Index
> Fetch the complete documentation index at: https://docs.struct.to/llms.txt
> Use this file to discover all available pages before exploring further.

# Events Stream

> Stream real-time Polymarket event rows with filter or ids mode and configurable cadence.

<Note>
  **Room ID:** `polymarket_events_stream` \
  **Endpoint:** `wss://api.struct.to/ws` \
  **Rate:** 0.025 credits per message
</Note>

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.

<Tip>
  **Related guide:** [Building a live trending feed](/guides/real-time-trending-events-and-markets) seeds from the events list and pushes deltas through this stream.
</Tip>

## 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

| Field | Type | Required | Description |
| - | - | - | - |
| `action` | `"subscribe" \| "unsubscribe" \| "unsubscribe_all"` | Yes | Slot lifecycle action. |
| `interval_ms` | `500 \| 1000 \| 3000 \| 10000` | For subscribe / unsubscribe | Flush cadence. Defaults to `1000` if omitted. |
| `mode` | `"filter" \| "ids"` | No | Subscription mode. Defaults to `filter`. |
| `filter` | `EventsStreamFilter` | `mode=filter` only | Filter body; all fields optional. |
| `event_slugs` | `string[]` | `mode=ids` only | Event slugs to watch. |
| `event_ids` | `string[]` | `mode=ids` only | Event 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

```json theme={null}
{
  "type": "join_room",
  "payload": {
    "room_id": "polymarket_events_stream"
  }
}
```

```json theme={null}
{
  "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

```json theme={null}
{
  "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

```json theme={null}
{
  "type": "room_message",
  "payload": {
    "room_id": "polymarket_events_stream",
    "message": {
      "action": "unsubscribe",
      "interval_ms": 500,
      "mode": "ids"
    }
  }
}
```

### Response

```json theme={null}
{
  "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

| Field | Type | Description |
| - | - | - |
| `type` | `"events_stream_update"` | Envelope discriminator. |
| `room_id` | `"polymarket_events_stream"` | Room identifier. |
| `mode` | `"filter" \| "ids"` | The mode this subscription was created with. |
| `interval_ms` | `500 \| 1000 \| 3000 \| 10000` | The cadence slot this event is flushed under. |
| `data` | `PolymarketEvent[]` | Full event rows, same shape as `GET /polymarket/events`. |

#### `PolymarketEvent`

| Field | Type | Description |
| - | - | - |
| `id` | `string` | Event ID. |
| `event_slug` | `string \| null` | URL-safe event slug. |
| `title` | `string \| null` | Human-readable title. |
| `ticker` | `string \| null` | Short ticker, when present. |
| `description` | `string \| null` | Long description. |
| `resolution_source` | `string \| null` | Resolution source URL. |
| `category` | `string \| null` | Primary category label. |
| `image_url` | `string \| null` | CDN image URL. |
| `market_count` | `integer` | Number of child markets. |
| `created_time` | `integer \| null` | Unix seconds. |
| `closed_time` | `integer \| null` | Unix seconds. |
| `start_time` | `integer \| null` | Unix seconds. |
| `end_time` | `integer \| null` | Unix seconds. |
| `neg_risk` | `boolean` | Whether this event uses the neg-risk market. |
| `neg_risk_market_id` | `string \| null` | Neg-risk market ID, when applicable. |
| `game_status` | `string \| null` | Sports-event game status. |
| `show_market_images` | `boolean` | Whether child market images should be shown. |
| `status` | `string \| null` | `"open"` or `"closed"`. |
| `metrics` | `Record<Timeframe, SimpleTimeframeMetrics>` | Keyed by timeframe (`1m`, `5m`, `30m`, `1h`, `6h`, `24h`, `7d`, `30d`). |
| `tags` | `PolymarketTag[]` | Event tags. |
| `markets` | `EventMarket[]` | Child markets with enriched outcomes. |
| `series` | `PolymarketSeries \| null` | Parent series, when present. |

#### `SimpleTimeframeMetrics`

| Field | Type | Description |
| - | - | - |
| `volume` | `number` | USD volume within the window. |
| `fees` | `number` | USD fees within the window. |
| `txns` | `integer` | Trade count within the window. |
| `unique_traders` | `integer` | Unique wallet count within the window. |

#### `PolymarketTag`

| Field | Type | Description |
| - | - | - |
| `id` | `string` | Tag ID. |
| `label` | `string` | Display label. |
| `slug` | `string \| null` | URL-safe slug. |

#### `EventMarket`

| Field | Type | Description |
| - | - | - |
| `condition_id` | `string` | 0x-prefixed condition ID. |
| `id` | `string \| null` | Market ID. |
| `title` | `string \| null` | Market title. |
| `question` | `string` | Market question. |
| `market_slug` | `string` | URL-safe market slug. |
| `status` | `string` | Market status. |
| `created_time` | `integer \| null` | Unix seconds. |
| `end_time` | `integer \| null` | Unix seconds. |
| `volume` | `number \| null` | Lifetime USD volume. |
| `liquidity_usd` | `number \| null` | Current USD liquidity. |
| `volume_24hr` | `number \| null` | 24h USD volume. |
| `image_url` | `string \| null` | CDN image URL. |
| `market_maker_address` | `string \| null` | Market maker contract. |
| `creator` | `string \| null` | Wallet address of creator. |
| `category` | `string \| null` | Category label. |
| `accepting_orders` | `boolean \| null` | Whether CLOB is accepting new orders. |
| `uma_resolution_status` | `string \| null` | UMA oracle resolution status. |
| `clob_rewards` | `ClobReward[]` | Active reward configs. |
| `outcomes` | `EventMarketOutcome[]` | Market outcomes. |
| `winning_outcome` | `EventMarketOutcome \| null` | Resolved winning outcome, when applicable. |

#### `EventMarketOutcome`

| Field | Type | Description |
| - | - | - |
| `name` | `string` | Outcome label (e.g. `"Yes"`). |
| `price` | `number \| null` | Latest price (0 – 1). |
| `position_id` | `string \| null` | ERC-1155 outcome token ID (decimal string). |
| `outcome_index` | `integer \| null` | 0-indexed outcome position. |

#### `PolymarketSeries`

| Field | Type | Description |
| - | - | - |
| `id` | `string` | Series ID. |
| `slug` | `string \| null` | URL-safe slug. |
| `ticker` | `string \| null` | Ticker, when present. |
| `title` | `string \| null` | Display title. |
| `description` | `string \| null` | Long description. |
| `series_type` | `string \| null` | Series type discriminator. |
| `recurrence` | `string \| null` | Recurrence cadence (e.g. `"daily"`). |
| `layout` | `string \| null` | UI layout hint. |
| `image_url` | `string \| null` | CDN image URL. |
| `icon_url` | `string \| null` | CDN icon URL. |
| `active` | `boolean` | Whether the series is currently active. |
| `closed` | `boolean` | Whether the series is closed. |
| `archived` | `boolean` | Whether archived. |
| `featured` | `boolean` | Whether featured in discovery surfaces. |
| `restricted` | `boolean` | Whether the series is region-restricted. |
| `pyth_token_id` | `string \| null` | Pyth price feed ID, when applicable. |
| `cg_asset_name` | `string \| null` | CoinGecko asset name, when applicable. |
| `start_date` | `integer \| null` | Unix seconds. |
| `event_count` | `integer` | Number of child events. |

See the [Markets Stream](/websockets/rooms/markets-stream) room for the `ClobReward` shape — it is shared across both stream rooms.

#### Example

```json theme={null}
{
  "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
    }
  ]
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.