Quick answer
With an Odds-API.io key and about 40 lines of Python you can list events, pull prices from several bookmakers, turn them into implied probabilities and stream live updates. The REST base URL is https://api.odds-api.io/v3 and the key goes in the apiKey query parameter [1] [2].
What you need
What you need: Python 3.9+, the requests and websockets packages, and an API key. New free keys are paused; plans start at £49/month with 2 bookmakers and 5,000 requests per hour [1] [3].
Get a key: Odds-API.io Partner link
Partner link — we may earn a commission. See our affiliate disclosure.
Code status: The Python in this tutorial was run against the example responses published in the Odds-API.io docs (checked 3 November 2026), not against a live API key. Live responses can carry extra fields; the code only reads documented ones.
1. Install the packages and store your key
Keep the key in an environment variable, not in your code [1].
python -m pip install requests websockets
export ODDS_API_KEY="your-api-key" # Windows PowerShell: $env:ODDS_API_KEY="your-api-key"
2. List sports and upcoming events
/sports needs no key. /events takes a sport plus optional league, status (e.g. pending, live), date range and limit [4].
import os
import requests
BASE = "https://api.odds-api.io/v3"
API_KEY = os.environ["ODDS_API_KEY"] # never hard-code or commit your key
BOOKMAKERS = "Bet365,Unibet" # use bookmakers selected on your plan
def get(path, **params):
"""GET an Odds-API.io v3 endpoint and return parsed JSON."""
params["apiKey"] = API_KEY
r = requests.get(BASE + path, params=params, timeout=20)
if r.status_code == 429:
raise RuntimeError("Rate limit reached - wait before retrying")
r.raise_for_status()
return r.json()
sports = requests.get(BASE + "/sports", timeout=20).json() # no key needed
print([s["slug"] for s in sports][:10])
events = get("/events", sport="football", league="england-premier-league",
status="pending", limit=10)
for e in events:
print(e["id"], e["date"], e["home"], "v", e["away"])
3. Fetch odds and compute implied probability
/odds takes one eventId, up to 30 bookmakers and an optional markets filter; prices are decimal strings [4] [1]. The snippet converts them to implied probabilities, removes the Overround proportionally and finds the best price per outcome — the same maths as the Implied probability & no-vig calculator.
def ml_prices(event_odds):
"""{bookmaker: {"home": 2.1, "draw": 3.4, "away": 3.2}} from one /odds response."""
out = {}
for book, markets in event_odds.get("bookmakers", {}).items():
for m in markets:
if m["name"] == "ML" and m.get("odds"):
row = m["odds"][0]
out[book] = {k: float(row[k]) for k in ("home", "draw", "away") if k in row}
return out
event_id = events[0]["id"]
prices = ml_prices(get("/odds", eventId=event_id, bookmakers=BOOKMAKERS, markets="ML"))
for book, p in prices.items():
booksum = sum(1 / o for o in p.values()) # implied probabilities
fair = {side: (1 / o) / booksum for side, o in p.items()} # proportional no-vig
print(f"{book}: overround {booksum - 1:.2%}",
{side: f"{q:.1%}" for side, q in fair.items()})
best = {}
for book, p in prices.items():
for side, o in p.items():
if o > best.get(side, (0, ""))[0]:
best[side] = (o, book)
print("Best price per outcome:", best)On the sample response in the docs (Bet365 2.10 / 3.40 / 3.20) this prints an overround of 8.28%.
4. Batch up to 10 events in one request
/odds/multi returns odds for up to 10 events and counts as one request, which matters with a 5,000 requests/hour budget [4] [3].
ids = ",".join(str(e["id"]) for e in events[:10]) # up to 10 events
for ev in get("/odds/multi", eventIds=ids, bookmakers=BOOKMAKERS, markets="ML"):
print(ev["home"], "v", ev["away"], ml_prices(ev))
5. Stream live prices over WebSocket
For prices that move in play, use the WebSocket feed at wss://api.odds-api.io/v3/ws. The markets filter is required, connections must support permessage-deflate (Python websockets does; websocket-client is refused), and each updated message carries the full market set, so replace what you store rather than merging [5]. WebSocket access is a paid add-on [3].
import asyncio, json, os
from urllib.parse import urlencode
import websockets # needs permessage-deflate; "websocket-client" is refused
STATE = {} # (event id, bookmaker) -> list of markets
async def stream():
qs = urlencode({"apiKey": os.environ["ODDS_API_KEY"], "markets": "ML",
"sport": "football", "status": "live"})
async with websockets.connect(f"wss://api.odds-api.io/v3/ws?{qs}",
ping_interval=30, ping_timeout=10) as ws:
async for raw in ws:
msg = json.loads(raw)
if msg["type"] in ("created", "updated"):
STATE[(msg["id"], msg["bookie"])] = msg["markets"] # replace, never merge
for m in msg["markets"]:
if m["name"] == "ML" and m.get("odds"):
print(msg["id"], msg["bookie"], m["odds"][0])
elif msg["type"] in ("deleted", "no_markets"):
STATE.pop((msg["id"], msg["bookie"]), None)
asyncio.run(stream())
6. Where to go next
Feed the prices into the Arbitrage calculator or Expected value (+EV) calculator, or let the API do it: see Arbitrage and +EV finder with Odds-API.io. To compare with Polymarket and Kalshi, read Prediction-market prices vs bookmaker odds.
Frequently asked questions
Do I need a paid plan?
Yes for new users: new free keys are paused, and plans start at £49 a month. Existing free keys keep working with 100 requests an hour, capped at 500 a day.
Which bookmakers can I query?
The ones selected on your plan. Ask for others and they will not be returned.
REST or WebSocket?
REST for snapshots and one-off lookups; WebSocket for continuously fresh prices. The docs recommend the WebSocket feed for live data.
About this guide
Pred-Data publishes independent, source-linked explainers about prediction markets and the AI tools used around them. Every factual claim on this page links to a primary or reputable secondary source, checked on the date shown above. We do not run a trading venue, take bets, or give personal advice.
Author: Ken Lin · Corrections: if you spot an error, we review it and date-stamp the fix.
Affiliate disclosure
Some links on this site (marked “Partner link”) may be affiliate links: if you sign up through them we may earn a commission at no extra cost to you. Partner links use rel="sponsored nofollow" and never change our assessment. Platform links in our comparison tables are plain, non-affiliate links to official sites.
Sources
- Quickstart (get a key, first calls, example responses) — Odds-API.io docs, accessed 2 Nov 2026
- AI & Vibe Coding (llms.txt, OpenAPI, copy-paste context, MCP setup) — Odds-API.io docs, accessed 2 Nov 2026
- Odds API Pricing (plans, WebSocket add-on, extra requests, payment) — Odds-API.io, accessed 2 Nov 2026
- OpenAPI specification (all v3 endpoints and parameters) — Odds-API.io docs, accessed 2 Nov 2026
- WebSocket guide (wss://api.odds-api.io/v3/ws, markets filter, reconnects) — Odds-API.io docs, accessed 2 Nov 2026