Live, streamed
Subscribe over WebSocket to a single in-play match or to a sport's entire live feed. Messages arrive when the line moves, not when your timer fires.
01 Pre-match and live, in real time
Live streamed over WebSocket: prices arrive the moment they move, not when your timer fires. Pre-match served by plain REST over cache. Full market book, score and match stats, seven sports. No scrapers, no proxies, no captchas, no bans.
Paid in crypto · no KYC · activated in 1–3 minutes
// an update message from /v1/stream
{
"event_id": 1633054685,
"sport": "football",
"league": "Argentina — Primera",
"home": "Barracas Central",
"away": "Aldosivi",
"live": true,
"running_state": "2H - 26'",
"elapsed": 26,
"score": { "home": 1, "away": 0 },
"stats": {
"corners": { "home": 1, "away": 8 },
"red_cards": { "home": 0, "away": 0 },
"yellow_cards": { "home": 1, "away": 0 }
},
"live_connected": true,
"markets": [
{ "key": "moneyline", "period_name": "match",
"is_main": true, "outcomes": [
{ "label": "1", "price": "1.246" },
{ "label": "X", "price": "4.780" },
{ "label": "2", "price": "24.750" }
] }
]
}
// connect
wss://api.pinnacle-odds-api.com/v1/stream?api_key=pk_live_…
// → subscribe to a single match
{ "op": "subscribe", "channel": "event",
"event_id": 1633054685 }
// → the whole live feed for a sport
{ "op": "subscribe", "channel": "live",
"sport": "football" }
// pre-match uses plain REST, no socket:
// GET /v1/football/leagues/brazil-serie-a/events
// GET /v1/events/1633043364
// ← immediately on subscribe: current state
{ "type": "snapshot", "seq": 1, "data": { … } }
// ← then only when the line actually moves
{ "type": "update", "seq": 2, "data": { … } }
import asyncio, json, websockets
URL = ("wss://api.pinnacle-odds-api.com/v1/stream"
"?api_key=pk_live_a1b2c3d4e5f6")
async def main():
async with websockets.connect(URL) as ws:
await ws.send(json.dumps({
"op": "subscribe",
"channel": "live",
"sport": "football",
}))
async for raw in ws:
msg = json.loads(raw)
if msg["type"] not in ("snapshot", "update"):
continue
for ev in msg["data"]["events"]:
print(ev["home"], ev["score_text"],
ev["away"], ev["running_state"])
asyncio.run(main())
02 Features
Built for real workloads: arbitrage and value scanners, trading bots, analytics dashboards and pricing models.
Subscribe over WebSocket to a single in-play match or to a sport's entire live feed. Messages arrive when the line moves, not when your timer fires.
However many clients watch the same match, exactly one upstream connection is opened. The line does not lag under load, and a new subscriber gets state instantly.
Moneyline, handicap, total and team totals across every period — full match, halves, sets, to-qualify — with both main and alternative lines.
Alongside the prices: score, score per period and per set, match clock, corners, yellow and red cards, and market suspension status.
Football, tennis, basketball, ice hockey, American football, baseball and esports. Each with a league catalogue using stable slug codes and filters.
One response shape: fixed market keys, decimal odds, UTC timestamps, a per-subscription seq counter. The source reshuffles its internals; you never notice.
A suspended outcome arrives priced "0" rather than vanishing from the payload. Your UI shows a lock instead of concluding the market does not exist.
A pool of mirrors with automatic failover, proxies, session rotation, rate-limit handling and 24/7 source monitoring. No account of yours, no scraper of yours.
Leagues, fixtures and pre-kickoff markets are served by ordinary requests over cache. Holding a socket open for a line that barely moves helps nobody.
03 How the stream works
The difference between polling a source and listening to a stream is the difference between a hundred requests a minute per match and one connection shared by all your users. The stream is for live; pre-match rides on plain REST, because before kickoff the line barely moves.
Open one WebSocket and subscribe to the matches and live feeds you need — up to 200 subscriptions per connection.
A snapshot with the current state arrives immediately, served from our memory. No waiting for the source's next tick.
After that, an update if and only if odds, score or match state have genuinely changed.
| Data | Channel | Refresh |
|---|---|---|
| In-play match book, score, stats | WS · event |
real time |
| Every in-play match of a sport | WS · live |
every 6 s |
| Leagues and fixtures | REST · /v1/{sport}/leagues |
on request |
| Pre-match market book | REST · /v1/events/{id} |
on request |
04 Endpoints
Base URL: https://api.pinnacle-odds-api.com ·
Auth: X-API-Key header
(WebSocket uses the api_key parameter) ·
Format: JSON.
| Method | Purpose | Parameters |
|---|---|---|
WS/v1/stream |
Live odds stream: live and event channels |
api_key |
GET/v1/sports |
Available sports | — |
GET/v1/{sport}/leagues |
League and tournament catalogue | locale, no_cache |
GET/v1/{sport}/leagues/{code}/events |
Events inside a league, pre-match and live | is_live, locale |
GET/v1/{sport}/live |
Every in-play match of a sport | locale |
GET/v1/events/{id} |
Full market book for a single match | only_main, period, locale |
GET/v1/raw/events/{id} |
Raw upstream payload for a match | locale |
GET/v1/me |
Your plan and remaining quota | — |
05 Pricing
No hidden fees, no auto-charges. Pay manually for the period you need — 10 % off for 3 months, 20 % off for 6.
For getting to know the API and small personal projects.
$49/ mo
For arbitrage scanners, value bots and active trading.
$149/ mo
/v1/raw/For customer-facing products under real load.
$399/ mo
Dedicated capacity and non-standard requirements.
Let's talk
| Feature | Starter | Pro | Business | Enterprise |
|---|---|---|---|---|
| Requests / month | 50 K | 500 K | 3 M | ∞ |
| Requests / second | 5 | 20 | 60 | 200 |
| Pre-match over REST | yes | yes | yes | yes |
| Live over WebSocket | — | yes | yes | yes |
| WebSocket connections | — | 3 | 10 | ∞ |
| Score and stats | — | yes | yes | yes |
| Raw JSON | — | yes | yes | yes |
| API keys | 1 | 2 | 5 | 20 |
06 Questions
You do not poll us in a loop — you receive a message the moment the line moves. Subscribing delivers a snapshot with the current state immediately, then only update messages when odds or the score actually change. Your quota stops depending on how often you poll, and your latency stops depending on your poll interval.
Before kickoff the line moves rarely, so a stream would push almost-empty traffic while tying up a connection on both ends. Pre-match is served by ordinary REST requests over cache — cheaper and simpler to integrate. Streaming kicks in where it earns its keep: in-play, where a price lives for seconds.
However many clients watch the same match, our server holds exactly one upstream connection and fans the data out to every subscriber from memory. The line does not lag under load, and a new subscriber receives the current state instantly instead of waiting for the next update.
Seven: football, tennis, basketball, ice hockey, American football, baseball and esports. Each with a league catalogue, events, and a full market book across every period and alternative line.
Moneyline, handicap, total and team totals across every match period (full match, halves, sets, to-qualify), with main and alternative lines. Each market carries a stable key, a period number, a main-line flag and a line_id.
true means the data is arriving over the source's WebSocket — real time. false means a fallback channel is in use and prices may lag by a few seconds. We surface this rather than hiding it behind a polished facade: for trading strategies the difference matters, and you should be able to see it.
Crypto only: USDT (TRC-20 and ERC-20), BTC, ETH, TON, LTC. You receive an address and an amount; once the transaction confirms on-chain your subscription activates automatically, typically within 1–3 minutes. There are no recurring charges.
The complete OpenAPI spec, every response schema and sample WebSocket messages are published in the documentation — no key, no sign-up. If you need a specific match or market broken down for your use case, message us and we'll send the real API response for that event.
No. The entire collection stack sits on our side: a pool of mirrors with automatic failover, proxies, session rotation and monitoring. You interact only with our endpoint and your API key.
No. The service returns odds data only and does not accept bets — bet placement is not implemented and is not planned. What you do with the data is your side of the line.
REST returns HTTP 429 with a Retry-After header; a WebSocket opened beyond your connection allowance is closed with code 4429. Your key is never banned — access resumes automatically, and limits can be raised at any time by paying the pro-rated difference.
On Starter and Pro the data is for your own products only. Reselling or granting third-party access is available on Business and Enterprise under separately agreed terms.
If the service is down beyond the stated SLA through our fault, we extend your subscription by a compensating period. A crypto refund is available within 48 hours of payment provided you have made fewer than 500 requests.
Message us — we'll match a plan to your load, send a payment address, and issue your key as soon as the transaction confirms. Usually a matter of minutes.