Prediction markets and sportsbooks price the same games, but they rarely agree exactly. Polymarket might have a team at 62 cents while the bookmakers imply 58%. Whether that gap means something depends on what you're doing with it, but you can't look at it without both prices side by side.
This post pulls both for one match and lines them up as implied probabilities. It uses the Odds-API.io Python SDK, which returns Polymarket and Kalshi prices in the same format as regular bookmakers. You need an API key (paid plans).
Setup
pip install odds-api-io
from odds_api import OddsAPIClient
client = OddsAPIClient(api_key="YOUR_API_KEY")
Find a match
events = client.get_events(sport="football", league="england-premier-league", status="pending")
event = events[0]
print(event["id"], event["home"], "vs", event["away"], event["date"])
Get odds from both worlds
books = "Polymarket,Kalshi,Bet365,Unibet,DraftKings,Betfair Exchange"
odds = client.get_event_odds(event_id=event["id"], bookmakers=books)
Polymarket and Kalshi count toward your plan's bookmaker limit like any other source, and names are case-sensitive (bet365 gets a 400). Every source comes back under odds["bookmakers"], keyed by name, each with a list of markets. For a football match, the ML market has home, draw and away as decimal odds. Prices arrive as strings. For Polymarket and Kalshi the API converts cents to decimal odds, so 60 cents comes back as 1.67.
Turn odds into probabilities
Decimal odds of 2.00 imply 50%. Bookmaker prices include a margin, so the three outcomes add up to more than 100%. Normalising removes it:
def implied(ml):
raw = {k: 1 / float(ml[k]) for k in ("home", "draw", "away") if ml.get(k)}
total = sum(raw.values())
return {k: v / total for k, v in raw.items()}
rows = {}
for book in books.split(","):
markets = odds["bookmakers"].get(book, [])
ml = next((m for m in markets if m["name"] == "ML"), None)
if ml and ml["odds"]:
rows[book] = implied(ml["odds"][0])
Compare
sportsbooks = [b for b in rows if b not in ("Polymarket", "Kalshi", "Betfair Exchange")]
consensus = {
k: sum(rows[b][k] for b in sportsbooks) / len(sportsbooks)
for k in ("home", "draw", "away")
}
print(f"{'source':<18}{'home':>8}{'draw':>8}{'away':>8}")
for book, p in rows.items():
print(f"{book:<18}" + "".join(f"{p.get(k, 0):>8.1%}" for k in ("home", "draw", "away")))
print(f"{'consensus':<18}" + "".join(f"{consensus[k]:>8.1%}" for k in ("home", "draw", "away")))
Betfair Exchange is an exchange rather than a bookmaker, so it stays in the table but out of the bookmaker average. Here is the output for Manchester City vs Fulham (Premier League, 21 November 2026), run on 12 November:
72221406 Manchester City vs Fulham FC 2026-11-21T12:30:00Z
source home draw away
Polymarket 67.3% 18.8% 13.9%
Kalshi 67.6% 18.6% 13.7%
Bet365 65.9% 19.7% 14.4%
Unibet 66.3% 19.5% 14.2%
DraftKings 65.7% 18.9% 15.4%
Betfair Exchange 67.4% 18.6% 14.1%
consensus 66.0% 19.4% 14.7%
Both prediction markets rate City about 1.5 points higher than the bookmaker average, and they sit close to the exchange. Differences of one or two points are normal noise. Bigger gaps usually come from timing (one side moved on news first) or from thin liquidity on the prediction market. For Polymarket and Kalshi the API also returns the other side of the book (layHome, layDraw, layAway) and the size at each price (depthHome, depthDraw, depthAway, plus depthLay...), which tells you how much is available at that price. On this match Kalshi showed 2,377 behind the City price and 16 on the draw, so a thin side can move on very little money.
Where to take it
- Run it on a schedule and log the gap over time, then check it against the result.
- Swap the REST call for the WebSocket feed if you want updates as prices move.
- Use historical closing lines to see whether the prediction market or the bookmakers were closer to the final price.
Docs for the prediction-market fields: https://docs.odds-api.io/guides/prediction-markets
If you want the bigger picture, we ran the same comparison on closing prices for 13,688 games: Polymarket vs Sportsbooks: How Often Do They Disagree?
I run Odds-API.io, so I'm biased toward the API used here.
Top comments (0)