Description
Stream the full real-time order book for non-sport prediction markets — politics, economics, finance, crypto, tech, culture, climate, health, geopolitics, companies, and other — across Kalshi and Polymarket. This is a Server-Sent Events (SSE) connection: instead of polling for updates, the server pushes a message every time a market's order book changes.
Unlike the sports odds stream (/stream/{sport}/odds), which sends incremental deltas, every message on this stream is a complete order book snapshot for a single market — bids, asks, best bid/ask, and spread. Consumers should replace their cached copy of that market on every message rather than patch it.
Best Practice: Don't Rely on canonical_id From the Stream
canonical_id From the StreamThe stream's
canonical_idfield is not populated — join to the mapping endpoints yourself, onmarket_id.
Each snapshot carries a canonical_id field, but it currently comes back as an empty string. The stream does not do the Kalshi↔Polymarket join for you in-line.
What to do instead
Build your canonical_market_id lookup separately from /prediction-markets/canonical-events, keyed on each platform's source_market_id. Then match it against the stream's market_id (formatted as <platform>:<source_market_id>, e.g. kalshi:AMAZONFTC-29DEC31 or polymarket:897017) to attach the canonical mapping to each incoming snapshot yourself.
Why this matters
If you wait on the stream to hand you a populated canonical_id, your pipeline will silently treat every market as unmapped. Do the join client-side using the REST mapping endpoints, and refresh that lookup periodically since /prediction-markets/canonical-events/ids can surface new events.
Specific Parameter Information
For a full list of parameters, scroll to the bottom of this page.
You must pass exactly one category. Authenticate with the X-Api-Key header.
category
(Required.) One of: politics, economics, finance, crypto, tech, culture, climate, health, geopolitics, companies, other. An invalid value returns a 4xx with the valid list.
Note: only one category is honored per connection. Passing it more than once (e.g. category=politics&category=culture) does not stream both — the last value wins and the earlier one is silently dropped. Open a separate connection per category if you need more than one.
platform
(Optional.) Filter to one platform: kalshi or polymarket. An invalid value returns a 4xx with the valid list. Omit to receive both.
Example Responses
Notes
This is a Server-Sent Events connection, so the "response" is a sequence of events rather than a single payload. You will see connected once on open, ping roughly every 5 seconds as a keepalive, and snapshot on every order book change for a market in the requested category.
Connected
event: connected
retry: 5000
data: ok go
Ping (keepalive, ~every 5s)
event: ping
retry: 5000
data: 2026-08-25T20:12:37Z
Snapshot — Kalshi
URL: https://api.opticodds.com/api/v3/stream/prediction-markets?category=politics&platform=kalshi
event: snapshot
retry: 5000
data: {
"entry_id": "",
"type": "snapshot",
"data": {
"market_id": "kalshi:AMAZONFTC-29DEC31",
"platform": "kalshi",
"source_market_id": "AMAZONFTC-29DEC31",
"source_event_id": "AMAZONFTC-29DEC31",
"outcomes": {
"yes": {
"token_id": "yes",
"best_bid": 0.42,
"best_ask": 0.45,
"spread": 0.03,
"last_trade_price": 0,
"tick_size": 0,
"bids": [
{ "price": 0.42, "size": 21.78 },
{ "price": 0.41, "size": 500 }
],
"asks": [
{ "price": 0.45, "size": 50 }
]
},
"no": {
"token_id": "no",
"best_bid": 0.55,
"best_ask": 0.58,
"spread": 0.03,
"last_trade_price": 0,
"tick_size": 0,
"bids": [
{ "price": 0.55, "size": 40 }
],
"asks": [
{ "price": 0.58, "size": 60 }
]
}
},
"timestamp_ns": 1787680501517000000,
"category": "politics",
"canonical_id": ""
}
}
Snapshot — Polymarket
URL: https://api.opticodds.com/api/v3/stream/prediction-markets?category=politics&platform=polymarket
event: snapshot
retry: 5000
data: {
"entry_id": "",
"type": "snapshot",
"data": {
"market_id": "polymarket:897017",
"platform": "polymarket",
"source_market_id": "897017",
"source_event_id": "100229",
"outcomes": {
"no": {
"token_id": "no",
"best_bid": 0.995,
"best_ask": 0.998,
"spread": 0.003,
"last_trade_price": 0,
"tick_size": 0,
"bids": [
{ "price": 0.995, "size": 448.12 },
{ "price": 0.99, "size": 35 }
],
"asks": [
{ "price": 0.998, "size": 20 }
]
},
"yes": {
"token_id": "yes",
"best_bid": 0.002,
"best_ask": 0.005,
"spread": 0.003,
"last_trade_price": 0,
"tick_size": 0,
"bids": [
{ "price": 0.002, "size": 500 }
],
"asks": [
{ "price": 0.005, "size": 700 }
]
}
},
"timestamp_ns": 1787680911430000000,
"category": "politics",
"canonical_id": ""
}
}