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

# WebSocket overview

> Stream real-time prediction market data over a single WebSocket connection.

Stream real-time, normalized prediction market data over WebSocket. One connection gives you cross-venue orderbooks, trades, and probability distributions from both Kalshi and Polymarket -- no venue-specific code needed.

Data is addressed by **what**, not where. You subscribe to `dist:fomc-2026-04-29` or `dist:btc-5m-2026-03-24-0830-et`, not a venue-specific ticker. Both venues arrive on the same channel, tagged by `venue` field.

Two feeds are available:

* [**Macro feed**](/websocket/macro-feed) — FOMC, CPI, NFP, GDP, unemployment, equities, commodities
* [**Crypto feed**](/websocket/crypto-feed) — BTC and ETH prediction markets enriched with real-time Binance spot/futures data

```text theme={null}
wss://feeds.oddpool.com/ws
```

## Authentication

Send an auth message with your API key after connecting. The server responds with your tier and limits.

```json theme={null}
// Send
{"action": "auth", "api_key": "oddpool_abc123..."}

// Response
{
  "type": "auth",
  "status": "ok",
  "user_id": 42,
  "tier": "pro",
  "limits": {
    "max_events": 10,
    "max_connections": 3,
    "channels": ["dist", "book", "trade", "snapshot"]
  }
}
```

`max_events: 0` means **unlimited** (Premium tier and above). Any positive number is the cap.

## Subscribe and unsubscribe

Subscribe to channels by event. Events count as concurrent subscriptions -- unsubscribing frees up slots.

### Channel format

| Format                         | Example                     | Scope                       |
| ------------------------------ | --------------------------- | --------------------------- |
| `{type}:{event_key}`           | `dist:fomc-2026-04-29`      | All outcomes for that event |
| `{type}:{event_key}:{outcome}` | `book:fomc-2026-04-29:hold` | Single outcome              |

### Example

```json theme={null}
// Subscribe to multiple channels at once
{"action": "subscribe", "channels": ["dist:fomc-2026-04-29", "book:fomc-2026-04-29:hold"]}

// Response
{"type": "subscribed", "channels": ["dist:fomc-2026-04-29", "book:fomc-2026-04-29:hold"]}

// Unsubscribe
{"action": "unsubscribe", "channels": ["book:fomc-2026-04-29:hold"]}
```

### Subscribe response shape

`channels` lists the channels that were actually subscribed. If any submitted channel was dropped, the response also includes a `rejected` array — only present when there is at least one rejection. Each entry has a `channel` and a `reason`:

| `reason`               | When it fires                                                                                                                                                                         |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unknown_event_key`    | The `event_key` is not currently served. Use [Catalog](/websocket/catalog) to find valid keys; venue-native IDs (Kalshi market tickers, Polymarket condition IDs) are not event keys. |
| `unknown_channel_type` | The prefix is not one of `dist`, `book`, `trade`, `snapshot`.                                                                                                                         |
| `missing_event_key`    | The channel string has no `event_key` after the type prefix.                                                                                                                          |

```json theme={null}
// Mixed batch
{"action": "subscribe", "channels": ["dist:fomc-2026-04-29", "dist:not-a-real-event"]}

// Response
{
  "type": "subscribed",
  "channels": ["dist:fomc-2026-04-29"],
  "rejected": [
    {"channel": "dist:not-a-real-event", "reason": "unknown_event_key", "event_key": "not-a-real-event"}
  ]
}
```

<Note>
  `dist`, `book`, and `trade` are change-driven — messages emit only when the underlying state changes, so an idle channel is normal during quiet windows. For current state on connect, also subscribe to `snapshot:{event_key}`, which delivers a full state message every 60 seconds.
</Note>
