World Cup Odds API: Live 2026 FIFA World Cup Odds from 350+ Bookmakers (Free)

World Cup Odds API - OddsPapi API Blog
How To Guides June 5, 2026

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.