Football Odds API: Real-Time Soccer Data
Why You Need a Football Odds API
Football is the world’s most-bet sport. If you’re building anything with betting data — a model, a scanner, a dashboard — you need odds from more than 20 bookmakers. You need the sharps. You need Asian Handicaps. And you need it in JSON, not scraped HTML.
Most APIs cover 40 soft bookmakers and call it a day. OddsPapi covers 140+ bookmakers per match, including Pinnacle, Singbet, and Betfair Exchange — the books that actually set the market. That’s 1X2, Asian Handicaps (every line), Both Teams To Score, Over/Under, and 460+ markets total. All on a free tier.
Football Odds Coverage: OddsPapi vs The Competition
| Feature | The Odds API | SportsGameOdds | OddsPapi |
|---|---|---|---|
| Bookmakers per match | ~15-20 | ~30 | 140+ |
| Sharp books (Pinnacle, Singbet) | No | No | Yes |
| Asian Handicap markets | Limited | Limited | Full (every line) |
| Over/Under lines | 2.5 only | Some | 1.5, 2.5, 3.5+ |
| BTTS market | Yes | Yes | Yes (109+ books) |
| Tournaments | ~30 leagues | ~50 leagues | 1,372 tournaments |
| Betfair Exchange | No | No | Yes |
| Free tier | 500 req/mo | Limited | 250 req/mo |
1,372 Tournaments, Every League That Matters
OddsPapi covers 1,372 football tournaments — from the Premier League, La Liga, Bundesliga, Serie A, and Ligue 1 down to lower divisions, youth leagues, and women’s football. Champions League, Europa League, MLS, Brasileiro Serie A, Eredivisie, Primeira Liga, Scottish Premiership, Turkish Super Lig, and hundreds more.
Coverage extends to every competition that bookmakers price. If a bookmaker prices it, we have it.
Football Markets: From 1X2 to Asian Handicaps
Each football fixture on OddsPapi can have 464+ unique markets. Here are the key ones with their market IDs — you’ll need these for API calls:
| Market | Market ID | Outcomes | Books |
|---|---|---|---|
| Full Time Result (1X2) | 101 | Home (101), Draw (102), Away (103) | 125+ |
| Both Teams To Score | 104 | Yes (104), No (105) | 109+ |
| Over/Under 2.5 Goals | 1010 | Over (1010), Under (1011) | 119+ |
| Over/Under 1.5 Goals | 1012 | Over (1012), Under (1013) | 101+ |
| Over/Under 3.5 Goals | 1014 | Over (1014), Under (1015) | 98+ |
| Asian Handicap -0.5 | 1068 | Home (1068), Away (1069) | 53 |
| Asian Handicap 0 | 1072 | Home (1072), Away (1073) | 22 |
| Asian Handicap -1 | 1064 | Home (1064), Away (1065) | 24 |
| Asian Handicap +0.5 | 1076 | Home (1076), Away (1077) | 30 |
Why Asian Handicaps Matter
Asian Handicaps are how sharps bet football. Each line has its own market ID — not one generic “Asian Handicap” market. This means you can compare Pinnacle’s -0.5 line directly against Bet365’s -0.5 line, or track how the -1.0 line moves across 24 bookmakers independently.
Other APIs collapse these into a single market or don’t cover them at all. OddsPapi gives you every line (from +0.5 through -1.5 and beyond) as a distinct, queryable market.
Python Tutorial: Football Odds in 5 Steps
Step 1: Setup
import requests
from datetime import datetime, timedelta
API_KEY = "YOUR_API_KEY" # Free at oddspapi.io
BASE_URL = "https://api.oddspapi.io/v4"
SPORT_ID = 10 # Soccer/Football
Step 2: Browse Tournaments
def get_tournaments():
"""Get all football tournaments."""
response = requests.get(f"{BASE_URL}/tournaments", params={
"apiKey": API_KEY,
"sportId": SPORT_ID
})
tournaments = response.json()
print(f"{len(tournaments)} tournaments available")
return tournaments
tournaments = get_tournaments()
# Filter to top leagues
for t in tournaments:
if any(name in t.get("tournamentName", "") for name in ["Premier League", "La Liga", "Bundesliga", "Serie A", "Champions"]):
print(f" {t['tournamentName']} (ID: {t['tournamentId']})")
Step 3: Get Today’s Fixtures
def get_fixtures(sport_id=SPORT_ID):
"""Get today's football fixtures with odds."""
today = datetime.now().strftime("%Y-%m-%d")
tomorrow = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d")
response = requests.get(f"{BASE_URL}/fixtures", params={
"apiKey": API_KEY,
"sportId": sport_id,
"from": today,
"to": tomorrow
})
fixtures = response.json()
with_odds = [f for f in fixtures if f.get("hasOdds")]
print(f"{len(with_odds)} fixtures with odds today")
return with_odds
fixtures = get_fixtures()
for f in fixtures[:10]:
print(f" {f['participant1Name']} vs {f['participant2Name']} ({f['tournamentName']})")
Step 4: Fetch Odds from 140+ Bookmakers
def get_odds(fixture_id):
"""Get odds from all bookmakers for a fixture."""
response = requests.get(f"{BASE_URL}/odds", params={
"apiKey": API_KEY,
"fixtureId": fixture_id
})
return response.json()
# Pick a fixture and get odds
fixture = fixtures[0]
odds = get_odds(fixture["fixtureId"])
bookmakers = list(odds.get("bookmakerOdds", {}).keys())
print(f"{len(bookmakers)} bookmakers pricing {fixture['participant1Name']} vs {fixture['participant2Name']}")
Step 5: Compare Odds Across Bookmakers
def compare_odds(odds_data, market_id="101"):
"""Compare odds across all bookmakers for a market.
Market 101 = Full Time Result (1X2)
Outcomes: 101=Home, 102=Draw, 103=Away
"""
results = []
for slug, bookie in odds_data.get("bookmakerOdds", {}).items():
market = bookie.get("markets", {}).get(market_id)
if not market:
continue
row = {"bookmaker": slug}
for outcome_id, outcome in market.get("outcomes", {}).items():
for player_id, player in outcome.get("players", {}).items():
price = player.get("price")
if outcome_id == "101":
row["home"] = price
elif outcome_id == "102":
row["draw"] = price
elif outcome_id == "103":
row["away"] = price
if "home" in row:
results.append(row)
# Sort by best home odds
results.sort(key=lambda x: x.get("home", 0), reverse=True)
print(f"\n1X2 Odds from {len(results)} bookmakers:")
print(f"{'Bookmaker':<20} {'Home':>8} {'Draw':>8} {'Away':>8}")
print("-" * 46)
for r in results[:10]:
print(f"{r['bookmaker']:<20} {r.get('home', 'N/A'):>8} {r.get('draw', 'N/A'):>8} {r.get('away', 'N/A'):>8}")
# Find best prices
best_home = max(results, key=lambda x: x.get("home", 0))
best_draw = max(results, key=lambda x: x.get("draw", 0))
best_away = max(results, key=lambda x: x.get("away", 0))
print(f"\nBest Home: {best_home['home']} @ {best_home['bookmaker']}")
print(f"Best Draw: {best_draw['draw']} @ {best_draw['bookmaker']}")
print(f"Best Away: {best_away['away']} @ {best_away['bookmaker']}")
compare_odds(odds)
Advanced: Compare Asian Handicap Lines
This is where it gets interesting for sharps. Pull Asian Handicap -0.5 odds from every bookmaker and compare:
def compare_asian_handicap(odds_data, market_id="1068"):
"""Compare Asian Handicap -0.5 across bookmakers.
Market 1068 = Asian Handicap -0.5
Outcomes: 1068=Home -0.5, 1069=Away +0.5
"""
results = []
for slug, bookie in odds_data.get("bookmakerOdds", {}).items():
market = bookie.get("markets", {}).get(market_id)
if not market:
continue
row = {"bookmaker": slug}
for outcome_id, outcome in market.get("outcomes", {}).items():
for player_id, player in outcome.get("players", {}).items():
price = player.get("price")
if outcome_id == "1068":
row["home_minus"] = price
elif outcome_id == "1069":
row["away_plus"] = price
if "home_minus" in row:
results.append(row)
results.sort(key=lambda x: x.get("home_minus", 0), reverse=True)
print(f"\nAsian Handicap -0.5 from {len(results)} bookmakers:")
print(f"{'Bookmaker':<20} {'Home -0.5':>10} {'Away +0.5':>10}")
print("-" * 42)
for r in results[:10]:
print(f"{r['bookmaker']:<20} {r.get('home_minus', 'N/A'):>10} {r.get('away_plus', 'N/A'):>10}")
compare_asian_handicap(odds)
This is where the edge lives. Soft bookmakers are slow to adjust Asian Handicap lines. Pinnacle moves first. If you can spot when Bet365’s -0.5 is still 1.95 but Pinnacle has already moved to 1.85, you’ve found value.
Free Historical Football Odds
Need to backtest your football model? Check Closing Line Value? Analyze how Asian Handicap lines moved before kickoff?
OddsPapi includes historical odds on the free tier. Pull past odds for any fixture, any bookmaker, any market. Most APIs charge extra for historical data — or lock it behind enterprise plans. We include it free because backtesting is how you build models that actually work.
Already using historical data? Check out our Bet365 Historical Odds Guide for a deep dive on strategy.
FAQ: Football Odds API
What sport ID is football/soccer?
Sport ID 10. Use sportId=10 in all requests. This covers all football/soccer tournaments worldwide — Premier League through to lower divisions.
How many bookmakers cover football?
Up to 140+ bookmakers per match for top leagues (Premier League, Bundesliga, Champions League). Lower leagues may have 30-50 bookmakers depending on the competition.
Do you cover Asian Handicaps?
Yes. Every line has its own market ID — from Asian Handicap +0.5 (market 1076) through -1.5 (market 1060) and beyond. Unlike other APIs, we don’t collapse them into one generic market. You get each line separately so you can compare the same handicap across bookmakers.
Is the API free?
The free tier gives you 250 requests per month. That’s enough to scan fixtures, pull odds, and compare prices across bookmakers. No credit card required.
Do you have Pinnacle football odds?
Yes. Pinnacle, Singbet, SBOBet, and other sharp bookmakers are included alongside 130+ soft books. You get the sharp prices that actually set the market.
What about live/in-play football odds?
Yes. Filter fixtures by status to find live matches. For real-time updates without polling, the Pro tier includes WebSocket streaming — odds pushed to your client the instant they change.
Start Building with Football Odds Data
Whether you’re building a model, scanning for value, or comparing lines across 140 bookmakers — start with 250 free API requests. No credit card, no enterprise sales call.
Get Your Free API Key at OddsPapi.io
Stop scraping. Stop paying for data that only covers soft books. Get the sharps, the Asian Handicaps, and the historical data — all in one API.