- Live scores, clocks, periods, and play state for every currently in-play game
- Real-time odds from 100+ sportsbooks including DraftKings, FanDuel, BetMGM, bet365, Polymarket, Novig, Sporttrade, Polymarket (USA), and more
- Polynode canonical slugs so the same game is identified the same way here as it is on polynode’s sports catalog
60-second quickstart
How a typical user lands here
You arrive knowing one of these things. Start with whichever applies:I know a SPORT
Start with List Sports, pick a category, drill into leagues.
I know a LEAGUE NAME
Use List Leagues to find the
pn_code by display name.I know a TEAM
Use Search Games to fuzzy-match on team names.
I have a Polymarket URL
Extract the slug from the URL, hit Game Detail directly.
Authentication
Every endpoint under/v1/* and /ws/* requires a pn_live_* API key — the same key you use for the rest of polynode. Only /health is public.
Two ways to pass the key:
HTTP 401 before the WebSocket upgrade completes.
Three key identifiers you’ll see
Every game has multiple ways to identify it. Understanding them upfront will make the rest of the docs make sense.game_id
Upstream’s canonical game id. Looks like 40664-16839-2026-04-14. Always present, stable, unique. Pass this to any /v1/games/{id} endpoint when you don’t have a polynode slug.
pn_slug
Polynode’s canonical game slug. Looks like lib-gar-est-2026-04-14 — {league}-{away}-{home}-{date}. Present for games where team resolution succeeded (~95% of major league games), null otherwise. Same slug that polynode’s own sports catalog uses. Pass this to any /v1/games/{id} endpoint interchangeably with game_id.
pn_league_code
Short league identifier. nba, nhl, epl, lib (Copa Libertadores), ipl (Indian Premier League), lol (League of Legends). Use List Leagues to discover the code for any league by its display name.
The 4 most important endpoints
| Endpoint | Purpose | Payload size |
|---|---|---|
GET /v1/games/live | Everything currently in-play | ~5-50 KB |
GET /v1/games/{id} | Full game + all market snapshots | ~50-200 KB |
GET /v1/games/{id}/scores | Just the scores for one game (cheap) | under 1 KB |
wss://.../ws/live | Push live score updates | streaming |
Discovery endpoints
When you don’t know what to query yet:| Endpoint | Answers |
|---|---|
GET /v1/sports | What sports do you cover? |
GET /v1/leagues | What leagues, and how many live games in each? |
GET /v1/market-types | What betting markets do you track? |
GET /v1/books | Which sportsbooks do you carry? |
GET /v1/games/search?q=... | Find a game by team name / slug / league |
Sample end-to-end flow
Say you want to find real-time NBA player points props for a specific game:What’s NOT in this API yet
Honest scope for v1:- Server-side subscription filters on the WebSocket channels — in v1 every subscriber receives every event on their channel and filters client-side. Subscription filters are on the roadmap for v1.1.
- Per-tier rate limits — all valid keys get unlimited access for now.
- Historical data — only currently-cached games (last 24 hours).
- Polymarket condition_id cross-reference — Polymarket prices flow through as just another
booknamed"Polymarket"or"Polymarket (USA)". A richer merge with polynode’s native PM orderbook data is a planned follow-up.
See also
- Live Games — every in-play game at a glance
- WebSocket Overview — push-based real-time stream
- Event Reference — full schema for every WS event type
Unmatched games: when pn_slug is null
Some games don’t have a polynode slug. This happens when upstream team names don’t match any team in polynode’s roster — for example:
- Esports (cs2, dota2, val, lcs, lpl) — polynode doesn’t carry esports team data, so nothing will match.
- Initialisms —
Liga Deportiva Universitaria de Quitovs polynode’sLDU de Quito. Pure name matching can’t bridge the initialism. - Transliterations — occasional Cyrillic/Arabic team names that don’t line up.
pn_slug is null, everything still works — you just use game_id as the identifier instead:
pn_slug: null, and every subsequent score_change / price_change event on that game carries its game_id. Filter client-side on game_id if you’re tracking a specific unmatched game:
game_id is the canonical identifier. pn_slug is a human-friendly alias. Build against game_id and you’ll never hit a dead end.
Rate limits
The REST API enforces 600 requests per minute per API key with a burst allowance of 60 requests. Requests over the limit return429 Too Many Requests with retry-after: 60. WebSocket connections are not counted — stream consumers don’t need to poll.
