World Cup Odds API: Live 2026 FIFA World Cup Odds from 350+ Bookmakers (Free)
The 2026 FIFA World Cup kicks off on June 11 with Mexico vs South Africa at the Estadio Azteca, then runs 104 matches across the USA, Canada and Mexico through July 19. If you’re building a model, a line-shopping tool, or just want to track where the sharp money is going, you need odds data — for every group-stage match, every knockout tie, all 48 teams.
The problem: FIFA’s official data partners (Sportradar, Genius Sports) sell that feed on enterprise contracts with a sales call attached. Scraping Bet365 breaks the moment they change a CSS class. And the generic “free” sports APIs give you ~20 soft bookmakers and zero sharps.
This guide shows you the third option: pull live World Cup odds from 350+ bookmakers — including Pinnacle, SBOBet, Bet365, DraftKings, FanDuel, plus prediction markets like Polymarket and Kalshi — through one JSON API, on a free tier that also includes historical data. Every code sample below was tested against the live feed three weeks before kickoff.
Why the "Old Way" Doesn’t Work for the World Cup
World Cup traffic is the single biggest spike in the betting calendar. Everyone wants the data at once, and the usual routes all have a catch:
| The Old Way | The Catch | OddsPapi |
|---|---|---|
| Official FIFA data (Sportradar / Genius) | Enterprise contract, sales call, opaque pricing | Self-serve API key, free tier |
| Scraping Bet365 / DraftKings | Breaks on every site change, IP bans, no sharps | Stable JSON, 350+ books in one call |
| Generic free sports APIs | ~20 soft books, no Pinnacle, no historical data | Pinnacle, SBOBet + prediction markets included |
| Manual odds entry | Stale within seconds during a match | Real-time, with full price history |
OddsPapi aggregates the books that actually matter to a sharp bettor or a quant: the sharps (Pinnacle, SBOBet) that set the market, the US books (DraftKings, FanDuel, BetMGM) your readers actually use, and the prediction markets (Polymarket, Kalshi) that frequently price tighter than the bookmakers. For a deeper dive on soccer specifically, see our Football Odds API guide.
What’s Already in the Feed
Three weeks out, every World Cup fixture already carries odds. On a marquee group match like Brazil vs Morocco, the feed returned 18 bookmakers on the Full Time Result, and Pinnacle alone priced 62 distinct markets on that single fixture. Here’s the spread of coverage:
| Market | Market ID | Books (3 weeks out) |
|---|---|---|
| Full Time Result (1X2) | 101 |
18 |
| Over/Under 2.5 Goals | 1010 |
15 |
| Both Teams To Score | 104 |
13 |
| Over/Under 1.5 / 3.5 | 108 / 1012 |
11 |
| Asian Handicap (multiple lines) | 1068, 1072, 1076… |
1–6 |
Book counts grow as kickoff approaches and margins tighten — the numbers above are an early-line snapshot, not the peak. By matchday you’ll see well over 100 books on the headline fixtures.
The Tutorial: World Cup Odds in Python
Four steps: authenticate, find the World Cup fixtures, pull the odds, and parse the nested JSON. You’ll need an OddsPapi API key (free) and the requests library.
Step 1: Authentication
OddsPapi uses a query-parameter API key, not a header. This trips up a lot of people coming from other APIs.
import requests
API_KEY = "YOUR_API_KEY"
BASE_URL = "https://api.oddspapi.io/v4"
# Auth is a query param, never a header
resp = requests.get(f"{BASE_URL}/sports", params={"apiKey": API_KEY})
print(resp.status_code) # 200
Step 2: Find the World Cup Fixtures
Soccer is sportId=10. The World Cup lives under the international category with tournamentSlug="world-cup". Fetch fixtures in a date window (max 10 days per call) and filter on the tournament name:
def get_world_cup_fixtures(date_from, date_to):
r = requests.get(f"{BASE_URL}/fixtures", params={
"apiKey": API_KEY,
"sportId": 10,
"from": date_from, # "2026-06-11"
"to": date_to, # "2026-06-20" (max 10 days apart)
})
fixtures = r.json()
# Filter to the FIFA World Cup tournament
wc = [f for f in fixtures if f.get("tournamentName") == "World Cup"]
return wc
matches = get_world_cup_fixtures("2026-06-11", "2026-06-20")
for f in matches:
print(f["startTime"][:10], f["participant1Name"], "vs",
f["participant2Name"], "| hasOdds:", f["hasOdds"])
# 2026-06-11 Mexico vs South Africa | hasOdds: True
# 2026-06-13 USA vs Paraguay | hasOdds: True
# 2026-06-13 Brazil vs Morocco | hasOdds: True
# ...
Only fixtures with hasOdds: True return a price payload — every World Cup match qualifies. Grab the fixtureId for the next step.
Step 3: Pull the Odds
Hit /odds with the fixtureId. Optionally pass a comma-separated bookmakers list to narrow the response:
def get_odds(fixture_id, bookmakers=None):
params = {"apiKey": API_KEY, "fixtureId": fixture_id}
if bookmakers:
params["bookmakers"] = bookmakers # "pinnacle,bet365,draftkings"
return requests.get(f"{BASE_URL}/odds", params=params).json()
# Brazil vs Morocco
odds = get_odds("id1000001666456928",
bookmakers="pinnacle,bet365,draftkings,fanduel,polymarket,kalshi")
books = odds["bookmakerOdds"]
print(f"{len(books)} bookmakers returned")
Step 4: Parse the Nested JSON
The /odds response is deeply nested. The path to a price is bookmakerOdds[slug]["markets"][marketId]["outcomes"][outcomeId]["players"]["0"]["price"]. For the Full Time Result (market 101), the outcome IDs are 101 = Home, 102 = Draw, 103 = Away.
def get_1x2(books, slug):
# Return (home, draw, away) decimal prices for a bookmaker, or None
try:
outs = books[slug]["markets"]["101"]["outcomes"]
return (
outs["101"]["players"]["0"]["price"], # Home
outs["102"]["players"]["0"]["price"], # Draw
outs["103"]["players"]["0"]["price"], # Away
)
except KeyError:
return None
for slug in ["pinnacle", "bet365", "draftkings", "fanduel", "polymarket", "kalshi"]:
line = get_1x2(books, slug)
if line:
print(f"{slug:11} Brazil {line[0]} Draw {line[1]} Morocco {line[2]}")
# pinnacle Brazil 1.625 Draw 3.79 Morocco 5.66
# bet365 Brazil 1.6 Draw 4.0 Morocco 5.25
# draftkings Brazil 1.62 Draw 3.95 Morocco 5.75
# fanduel Brazil 1.56 Draw 4.0 Morocco 5.6
# polymarket Brazil 1.639 Draw 4.348 Morocco 5.556
# kalshi Brazil 1.613 Draw 4.348 Morocco 5.556
Note the prediction markets. Polymarket’s prices on OddsPapi are already converted to decimal odds (not the native 0–1 share price), so you can drop them straight into a comparison alongside the sportsbooks.
Line Shopping: Find the Best World Cup Price
With 350+ books in one payload, finding the best available price for any outcome is a loop. Always filter on active — suspended or stale prices will otherwise poison your scan.
def best_price(books, market_id, outcome_id):
best_slug, best_odds = None, 0.0
for slug, bd in books.items():
try:
o = bd["markets"][str(market_id)]["outcomes"][str(outcome_id)]["players"]["0"]
if o.get("active") and o["price"] > best_odds:
best_slug, best_odds = slug, o["price"]
except (KeyError, TypeError):
continue
return best_slug, best_odds
# Best price on each Brazil vs Morocco outcome (market 101)
for oid, label in [("101", "Brazil"), ("102", "Draw"), ("103", "Morocco")]:
slug, price = best_price(books, 101, oid)
print(f"Best {label}: {price} @ {slug}")
# Best Brazil: 1.639 @ polymarket
# Best Draw: 4.348 @ kalshi
# Best Morocco: 5.8 @ circasports
For a production-grade version of this with sharp-book whitelists and arb-sum sanity checks, see our Python line shopping guide.
De-Vig: What’s the Fair Price?
Pinnacle is the sharpest book in the market, so its no-vig line is the closest thing to a fair probability you’ll find. Strip the margin by normalising the implied probabilities:
def devig_1x2(home, draw, away):
imp = [1/home, 1/draw, 1/away]
total = sum(imp) # the overround (> 1)
fair = [p/total for p in imp] # normalised true probabilities
fair_odds = [1/p for p in fair]
return fair, fair_odds, (total - 1) * 100
h, d, a = get_1x2(books, "pinnacle") # 1.625, 3.79, 5.66
fair, fair_odds, vig = devig_1x2(h, d, a)
print(f"Pinnacle margin: {vig:.2f}%")
print(f"Fair Brazil {fair[0]*100:.1f}% ({fair_odds[0]:.3f})")
print(f"Fair Draw {fair[1]*100:.1f}% ({fair_odds[1]:.3f})")
print(f"Fair Morocco {fair[2]*100:.1f}% ({fair_odds[2]:.3f})")
# Pinnacle margin: 5.59%
# Fair Brazil 58.3% (1.716)
# Fair Draw 25.0% (4.002)
# Fair Morocco 16.7% (5.976)
Interesting wrinkle in the early lines: the prediction markets were pricing tighter than Pinnacle. On Brazil vs Morocco, Polymarket’s 1X2 overround was 2.01% and Kalshi’s 2.99%, versus Pinnacle’s 5.59% and Bet365’s 6.55%. Early-line margins are always wider than they’ll be on matchday, but the gap is a useful reminder to always check where the sharpest implied probability is coming from. Our consensus odds calculator shows how to blend all 350+ books into a single fair line.
Free Historical Odds for Backtesting
This is the differentiator most APIs charge for. The /historical-odds endpoint returns the full price history for a fixture — every snapshot, with timestamps — on the free tier. Note the response shape differs from /odds: the top-level key is bookmakers (not bookmakerOdds), and players["0"] is a list of snapshots, not a single dict. Max 3 bookmakers per call.
def get_history(fixture_id, bookmakers):
r = requests.get(f"{BASE_URL}/historical-odds", params={
"apiKey": API_KEY,
"fixtureId": fixture_id,
"bookmakers": bookmakers, # max 3, e.g. "pinnacle,bet365,draftkings"
})
return r.json()
hist = get_history("id1000001666456928", "pinnacle,bet365,draftkings")
# players["0"] is a LIST of price snapshots:
snaps = hist["bookmakers"]["pinnacle"]["markets"]["101"]["outcomes"]["101"]["players"]["0"]
for s in snaps[:5]:
print(s["createdAt"], s["price"])
Use this to measure Closing Line Value, track how the market moved on each team, or backtest a model against three weeks of group-stage drift. Pipe it to a spreadsheet with our CSV/Excel export guide.
Beyond 1X2: Goals, BTTS & Asian Handicaps
The World Cup feed carries the full native market structure, not just match result. Look market names up via /v4/markets?sportId=10 rather than hardcoding the full catalog:
| Market | Market ID | Outcome IDs |
|---|---|---|
| Over/Under 2.5 Goals | 1010 |
1010 Over / 1011 Under |
| Both Teams To Score | 104 |
Yes / No |
| Asian Handicap 0 (Draw No Bet) | 1072 |
Home / Away |
| Asian Handicap -0.5 | 1068 |
Home / Away |
Asian Handicaps are handled natively — no awkward two-way conversion. For the cross-book AH scanner, see our Asian Handicap API guide.
Get Your Free API Key
The World Cup is the highest-volume betting event of the four-year cycle. Whether you’re building a value scanner, a live dashboard, or a model to beat the closing line, OddsPapi gives you 350+ bookmakers, sharps, prediction markets, and free historical data through one JSON endpoint — no enterprise contract, no scraping.
Stop scraping. Get your free OddsPapi API key →
Frequently Asked Questions
Does OddsPapi have 2026 World Cup odds?
Yes. All World Cup fixtures are in the feed with hasOdds: true, filterable by tournamentName="World Cup" (slug world-cup, category international) under soccer (sportId=10). Coverage includes group stage and knockout matches with 1X2, totals, BTTS and Asian Handicap markets.
Which bookmakers are covered for the World Cup?
350+ books including sharps (Pinnacle, SBOBet), US books (DraftKings, FanDuel, BetMGM), UK/EU books (Bet365), and prediction markets (Polymarket, Kalshi). A marquee group match like Brazil vs Morocco returned 18 books on the match result three weeks before kickoff, climbing past 100 by matchday.
Is the World Cup odds data free?
Yes — the free tier includes live odds across 350+ bookmakers and historical price history, with a ~0.88s cooldown between calls to the same endpoint. No paid plan is required for the tutorial code in this guide.
How do I get the World Cup winner / outright odds?
Outright tournament-winner markets are listed in the /v4/markets?sportId=10 catalog; query the catalog to find the current market ID, then pass the relevant fixtureId to /odds. Match-level markets (1X2, totals, handicaps) are available on every fixture as shown above.
Can I get historical World Cup odds for backtesting?
Yes. The /historical-odds endpoint returns the full snapshot history per fixture (up to 3 bookmakers per call) on the free tier. Use it to measure Closing Line Value or backtest a model against the group-stage market.