Introduction
Directly scraping commercial betting sites violates their Terms of Service (ToS) and carries legal risks. This quick guide shows you how to safely and legally fetch sports odds using a free, official public API (The Odds API) instead of an aggressive web scraper.
Install the required libraries:
pip install requests pandas
import os import requests import pandas as pd import time
Configuration
API_KEY = os.getenv("THE_ODDS_API_KEY", "YOUR_API_KEY_HERE") SPORT = "soccer_epl" # English Premier League REGIONS = "uk" # Bookmaker region MARKETS = "h2h" # Head-to-Head (Home/Draw/Away) ODDS_FORMAT = "decimal" # e.g., 1.50, 3.20
def fetch_odds_data(): url = f"https://api.the-odds-api.com/v4/sports/{SPORT}/odds" params = {"apiKey": API_KEY, "regions": REGIONS, "markets": MARKETS, "oddsFormat": ODDS_FORMAT}
try:
response = requests.get(url, params=params, timeout=10) # 10s timeout for safety
if response.status_code == 200:
return response.json()
elif response.status_code == 429:
print("Rate limit hit. Cooling down...")
time.sleep(60)
else:
print(f"Error: Status code {response.status_code}")
return None
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
return None
def parse_odds(json_data): if not json_data: return None parsed_data = []
for match in json_data:
home_team = match.get("home_team")
away_team = match.get("away_team")
commence_time = match.get("commence_time")
for bookmaker in match.get("bookmakers", []):
bookmaker_name = bookmaker.get("title")
for market in bookmaker.get("markets", []):
if market.get("key") == "h2h":
outcomes = market.get("outcomes", [])
home_odds = next((o["price"] for o in outcomes if o["name"] == home_team), None)
away_odds = next((o["price"] for o in outcomes if o["name"] == away_team), None)
draw_odds = next((o["price"] for o in outcomes if o["name"] == "Draw"), None)
parsed_data.append({
"Match Time": commence_time, "Home Team": home_team, "Away Team": away_team,
"Bookmaker": bookmaker_name, "Home Odds": home_odds, "Draw Odds": draw_odds, "Away Odds": away_odds
})
return pd.DataFrame(parsed_data)
if name == "main": raw_data = fetch_odds_data() df = parse_odds(raw_data)
if df is not None and not df.empty:
print(df.head()) # Preview data
df.to_csv("sports_odds.csv", index=False, encoding="utf-8-sig")
print("Saved to 'sports_odds.csv'")
Key Compliance Highlights
Zero Scraping: By querying an official API endpoints rather than HTML parsing front-ends, you never risk an IP ban or breaking target infrastructure.
Timeout & Back-off: Includes a timeout=10 constraint and automatically handles HTTP 429 (Too Many Requests) by pausing the execution thread.https://wow88.my