Overview
Predexon separates sports market discovery from live game state.
Market discovery answers: what can I trade?
Live state answers: what is happening in the underlying game?
That separation keeps identifiers clear:
| Identifier | Meaning | Example |
|---|
game_id | Broad real-world game | mlb-ari-pit-2026-05-07 |
event_id | Canonical market/question attached to a game | mlb-ari-pit-2026-05-07 for current game_winner v1 |
predexon_id | Canonical tradable outcome | px-xz7fw6lsywv2rnwr |
Live state is keyed by game_id, not event_id, because future spreads, totals, and props can all share the same live score while using different market/question IDs.
predexon_id is now canonical. It identifies one Predexon outcome across matched venues; it is not a venue-native token ID, ticker, condition ID, or legacy per-venue identifier.
Availability
| Surface | Status | Purpose |
|---|
GET /v2/sports/live | Available | List live-state snapshots by game. |
GET /v2/sports/live/{game_id} | Available | Fetch one live-state snapshot. |
wss://wss.predexon.com/v1/<API_KEY> | WebSocket | Stream aggregate live-state changes on the sports_live channel. |
If live state is not yet available for a game, REST can return the game shell with reliability.state = "no_data" and null score fields.
Recommended Integration
For an application that displays sports markets with live score context:
1. GET /v2/sports/games?league=mlb
-> discover games and canonical predexon_ids
2. GET /v2/sports/live?league=mlb
-> hydrate visible games with score/status snapshots
3. Subscribe to WSS by league or game_ids
-> receive aggregate live-state changes
4. GET /v2/sports/outcomes/{predexon_id}
-> resolve execution venues when the user trades
Do not use live state as a tradable identifier. Trading and routing continue to use outcome-level predexon_id.
Canonical predexon_id
For sports, predexon_id lives at the canonical outcome layer:
game_id -> broad real-world game
event_id -> canonical market/question
predexon_id -> canonical tradable outcome
token_id -> venue-native executable token, when a venue has one
market_ticker -> Kalshi venue-native ticker
condition_id -> CTF condition ID, not an outcome ID
Example:
game_id: mlb-ari-pit-2026-05-07
event_id: mlb-ari-pit-2026-05-07
outcome: ari
predexon_id: px-xz7fw6lsywv2rnwr
If Kalshi, Polymarket, and Predict.fun all list “Arizona Diamondbacks win” for that game, each matched venue listing returns the same canonical predexon_id.
Use predexon_id for routing, positions, orderbook subscriptions, and GET /v2/sports/outcomes/{predexon_id}. Use venue-native identifiers only when executing directly on a specific venue.
Data Sources
Sports live state v1 sources data from Polymarket and Kalshi. Predexon normalizes those updates into one stable game-state object keyed by game_id.
We are integrating with additional sports data providers soon to expand coverage and improve source redundancy.
Game Coverage
Sports live state covers Predexon’s matched canonical sports games: the same game universe exposed by GET /v2/sports/games.
Use the discovery endpoints to get the current event list:
GET /v2/sports/games
GET /v2/sports/games?league=mlb
GET /v2/sports/live
The websocket all-games subscription follows that same matched sports coverage:
{
"action": "subscribe",
"platform": "predexon",
"version": 1,
"type": "sports_live",
"filters": {
"game_ids": ["*"]
}
}
Current sports market coverage follows the venue coverage matrix. Live-state data is available when one of the live-state sources has data for that covered game.
Key Incidents and Event Lists
The live-state object includes a normalized event list in significant_events[].
significant_events[] is for key incidents, not full box-score or every vendor-specific stat. When source data is available, events can include:
| Event type | Description |
|---|
score_change | A scoring event, including soccer goals, basketball made shots/free throws, or other source-supported score changes. |
yellow_card | Soccer yellow card. |
red_card | Soccer red card. |
substitution | Soccer substitution or other source-supplied substitution event. |
period_start | Start of a period, half, quarter, map, or similar segment when exposed by the source. |
period_end | End of a period, half, quarter, map, or similar segment when exposed by the source. |
game_start | Game started. |
game_end | Game ended or final status reached. |
other | Source-supplied incident that does not map cleanly to a standard type yet. |
Example:
{
"type": "score_change",
"team": "home",
"period": "2H",
"clock": "58'",
"player": "Ramos, Guilherme",
"description": "Goal for the home team",
"source": "kalshi"
}
Incident availability is source- and sport-dependent. Kalshi can provide detailed game-stat/play-by-play data for supported milestones. In the current Kalshi feed, soccer commonly includes goals, cards, substitutions, period events, and game-end events; basketball can include made shots/free throws and period boundaries; baseball play-by-play is available for some games but is not always returned as typed key incidents. Polymarket’s sports websocket primarily provides score, period, status, clock, and game-level state. If no key incidents are available for a game, significant_events is returned as an empty array.
Aggregate State
The aggregate state is the primary client object.
{
"game_id": "mlb-ari-pit-2026-05-07",
"league": "mlb",
"sport": "baseball",
"game_date": "2026-05-07",
"title": "Arizona Diamondbacks vs Pittsburgh Pirates",
"home_team": {
"code": "pit",
"name": "Pittsburgh Pirates"
},
"away_team": {
"code": "ari",
"name": "Arizona Diamondbacks"
},
"participants": [
{ "code": "ari", "name": "Arizona Diamondbacks" },
{ "code": "pit", "name": "Pittsburgh Pirates" }
],
"status": "InProgress",
"status_text": "In Progress",
"live": true,
"ended": false,
"period": "Top 5",
"clock": null,
"score": {
"home": 2,
"away": 4,
"display": "2-4"
},
"winner": null,
"last_play": null,
"significant_events": [],
"reliability": {
"state": "single_source",
"source_count": 1,
"sources": ["polymarket"],
"conflict": false,
"stale": false
},
"updated_at": "2026-05-07T23:42:10Z"
}
Field Semantics
| Field | Meaning |
|---|
game_id | Broad sports fixture ID. Same value used by /v2/sports/games. |
home_team / away_team | Home/away teams when source mapping is known. May be null before source mapping is available. |
participants | Known participants from canonical game metadata. Present even when home/away is unknown. |
status | Normalized or source-derived game status. |
status_text | Human-readable status when available. |
live | Whether the game is currently in progress. |
ended | Whether the game has ended. |
period | Sport-specific period: inning, quarter, half, map number, etc. |
clock | Sport-specific clock when available. May be null for sports like baseball. |
score.home / score.away | Score by home/away team when home/away mapping is known. |
score.display | Source or aggregate display score. |
winner | Winner when known: typically home, away, draw, or a source-native value. |
last_play | Latest notable play when available. When present, it maps to the most recent item in significant_events. It can be null when no notable incident has happened yet or the source has not supplied one at that time. |
significant_events | Normalized key incident list. Can include score changes, cards, substitutions, period events, game start/end, or source-supported other events. Empty when no incidents are available. |
reliability | Source agreement and freshness summary. |
updated_at | Last aggregate update timestamp. |
Reliability
reliability is not a model confidence score. It is a data-quality summary based on direct source names, source agreement, and freshness.
| State | Meaning |
|---|
no_data | The game exists, but no source has written live state. |
single_source | One source has current state. |
confirmed | Two or more sources agree on material fields. |
conflict | Sources disagree on material fields such as score, winner, or ended status. |
stale | Latest available source state is too old for a live game. |
The boolean fields make the state easy to consume:
| Field | Meaning |
|---|
sources | Direct source names used for the aggregate, such as polymarket or kalshi. |
source_count | Number of source snapshots used. Equal to sources.length when source names are available. |
conflict | Whether material source disagreement exists. |
stale | Whether the aggregate is stale or absent. |
For normal product surfaces, use the aggregate fields and reliability. For debugging or auditing, request source rows with include_sources=true.
Source Snapshots
REST can include source-level rows:
GET /v2/sports/live/{game_id}?include_sources=true
Source snapshots are intended for audit/debug, not primary UI rendering.
{
"source": "polymarket",
"source_game_id": "19439",
"source_url": null,
"status": "InProgress",
"status_text": "InProgress",
"live": true,
"ended": false,
"period": "Top 5",
"clock": null,
"score": {
"home": 2,
"away": 4,
"display": "2-4"
},
"winner": null,
"last_play": null,
"significant_events": [],
"details": {
"slug": "mlb-pit-ari-2026-05-07"
},
"observed_at": "2026-05-07T23:42:09Z",
"updated_at": "2026-05-07T23:42:10Z"
}
WebSocket
The websocket streams aggregate changes, not every raw upstream source message. This keeps client integrations simple and avoids making consumers reconcile Polymarket/Kalshi differences themselves.
Endpoint:
wss://wss.predexon.com/v1/<API_KEY>
Authentication uses the same data key as REST, passed in the websocket URL:
wss://wss.predexon.com/v1/YOUR_DATA_KEY
Subscription Messages
Subscribe to all matched sports games Predexon currently covers:
{
"action": "subscribe",
"platform": "predexon",
"version": 1,
"type": "sports_live",
"filters": {
"game_ids": ["*"]
}
}
Subscribe by league:
{
"action": "subscribe",
"platform": "predexon",
"version": 1,
"type": "sports_live",
"filters": {
"leagues": ["mlb"]
}
}
Subscribe by explicit games:
{
"action": "subscribe",
"platform": "predexon",
"version": 1,
"type": "sports_live",
"filters": {
"game_ids": ["mlb-ari-pit-2026-05-07"]
}
}
Filters:
| Field | Type | Meaning |
|---|
game_ids | string[] | Subscribe to specific games. Use ["*"] for all matched games. |
leagues | string[] | Subscribe to all games in one or more leagues. |
sports | string[] | Subscribe to all games in one or more sports. |
Exactly one of game_ids, leagues, or sports is required.
Unsubscribe:
{
"action": "unsubscribe",
"subscription_id": "sub_01"
}
Server Messages
Subscription acknowledgement:
{
"type": "ack",
"subscription_id": "sub_01",
"channel": "sports_live"
}
Aggregate update:
{
"type": "event",
"subscription_id": "sub_01",
"data": {
"event_type": "sports_live_state",
"sequence": 1842,
"game_id": "mlb-ari-pit-2026-05-07",
"league": "mlb",
"sport": "baseball",
"home_team": {
"code": "pit",
"name": "Pittsburgh Pirates"
},
"away_team": {
"code": "ari",
"name": "Arizona Diamondbacks"
},
"updated_at": "2026-05-07T23:42:10Z",
"status": "InProgress",
"live": true,
"ended": false,
"period": "Top 5",
"clock": null,
"score": {
"home": 2,
"away": 4,
"display": "2-4"
},
"winner": null,
"reliability": {
"state": "single_source",
"source_count": 1,
"sources": ["polymarket"],
"conflict": false,
"stale": false
}
}
}
Error:
{
"type": "error",
"code": "INVALID_FILTERS",
"message": "Sports live subscriptions require exactly one of game_ids, leagues, or sports"
}
Heartbeats and Reconnects
The gateway uses websocket ping/pong frames for connection liveness. Clients should respond to ping frames according to their websocket library’s normal behavior.
If the connection drops, clients should reconnect and resubscribe. The REST endpoint should be used to rehydrate current state after reconnect:
GET /v2/sports/live?league=mlb
The websocket version does not require clients to track replay cursors. If replay is added later, it should be additive through a field such as since_sequence.
Update Policy
The websocket emits an update when the aggregate changes materially:
- Score changed
- Period changed
- Clock changed
- Key incidents changed
live or ended changed
- Winner changed
- Reliability changed
- Source conflict appeared or resolved
- Stale state appeared or resolved
The websocket does not emit every upstream heartbeat or duplicate source message.
Sports-Specific Notes
| Sport | Common period / clock behavior |
|---|
| MLB | period may be inning state such as Top 5; clock is usually null. |
| NBA/NFL/CFB | period is quarter; clock is game clock when supplied. |
| Soccer | period is half/status; clock may be elapsed match time. |
| NHL | period is period/overtime/shootout; clock is game clock when supplied. |
| Tennis/esports | period may be set/map number; score display can be source-specific. |
Non-Goals
The live-state API is not intended to expose every possible vendor-specific stat.
The stable public contract is:
- Current score
- Period/clock/status
- Winner/end state
- Significant events when available
- Source reliability
- Optional source snapshots for audit/debug
Full play-by-play, detailed box scores, and sport-specific stat feeds may be added later as separate endpoints if needed.