How to Get Football Data with Python - Complete API Tutorial
Step-by-step Python tutorial for fetching football match data, player stats, fixtures, and live odds via API. Includes working code examples you can copy.
Python is one of the best languages for working with football data. Analysts use it for notebooks and models. Backend developers use it for data pipelines. Betting and fantasy teams use it to collect, clean, and score match data.
This tutorial shows how to fetch football data with Python using TheStatsAPI. You will fetch competitions, fixtures, match stats, player season stats, pre-match odds, live odds, and paginated results.
Prerequisites
You need:
- Python 3.9+
requests- A TheStatsAPI API key
Install requests:
python -m pip install requests
Set your API key as an environment variable:
export THESTATSAPI_KEY="your_api_key"
Create a Small API Client
Create football_api.py:
import os
import requests
API_KEY = os.environ["THESTATSAPI_KEY"]
BASE_URL = "https://api.thestatsapi.com/api"
def get(endpoint, params=None):
response = requests.get(
f"{BASE_URL}{endpoint}",
headers={
"Authorization": f"Bearer {API_KEY}",
"Accept": "application/json",
},
params=params or {},
timeout=20,
)
response.raise_for_status()
return response.json()
Every example below can reuse this get() function.
Fetch Competitions
Start by listing competitions and finding the competition ID you need.
from football_api import get
result = get("/football/competitions", {"search": "Premier League"})
for competition in result["data"]:
print(competition["id"], competition["name"], competition["country"])
Typical output:
comp_3039 Premier League England
Save the competition ID. You will use it in match and season queries.
Get Fixtures for a League
Fetch matches for a competition:
from football_api import get
competition_id = "comp_3039"
result = get(
"/football/matches",
{
"competition_id": competition_id,
"per_page": 10,
},
)
for match in result["data"]:
home = match["home_team"]["name"]
away = match["away_team"]["name"]
status = match["status"]
date = match["utc_date"]
print(f"{date} - {home} vs {away} ({status})")
Use date_from, date_to, season_id, team_id, and status when you need narrower data pulls.
Handle Pagination
Most list endpoints return meta with page information. Use it to fetch all pages.
from football_api import get
def get_all(endpoint, params=None):
params = dict(params or {})
page = 1
rows = []
while True:
params["page"] = page
result = get(endpoint, params)
rows.extend(result["data"])
meta = result.get("meta", {})
if page >= meta.get("total_pages", 1):
break
page += 1
return rows
matches = get_all("/football/matches", {"competition_id": "comp_3039"})
print(f"Fetched {len(matches)} matches")
Use this pattern for data-sync jobs. For user-facing apps, cache the results in your database.
Get Match Stats
Match stats include shots, possession, corners, xG, non-penalty xG, passing, defending, and goalkeeping fields where available.
from football_api import get
match_id = "mt_010249745"
stats = get(f"/football/matches/{match_id}/stats")["data"]
overview = stats["overview"]
shots = overview["total_shots"]["all"]
possession = overview["ball_possession"]["all"]
xg = overview["expected_goals"]["all"]
print("Shots:", shots["home"], "-", shots["away"])
print("Possession:", possession["home"], "-", possession["away"])
print("xG:", xg["home"], "-", xg["away"])
This is the endpoint to enrich match pages, build form models, and calculate team-level features.
Get Player Season Stats
Player season stats are useful for fantasy football apps, player cards, scouting tools, and leaderboards.
from football_api import get
player_id = "pl_61928103"
season_id = "sn_6125938"
stats = get(
f"/football/players/{player_id}/stats",
{"season_id": season_id},
)["data"]
print("Appearances:", stats["appearances"])
print("Minutes:", stats["minutes_played"])
print("Goals:", stats["scoring"]["goals"])
print("Assists:", stats["scoring"]["assists"])
print("Yellow cards:", stats["discipline"]["yellow_cards"])
print("Red cards:", stats["discipline"]["red_cards"])
If you need match-by-match player data, use /football/matches/{match_id}/player-stats instead.
Get Pre-Match Odds
Pre-match odds are keyed by match and bookmaker.
from football_api import get
match_id = "mt_010249745"
odds = get(f"/football/matches/{match_id}/odds")["data"]
for book in odds.get("bookmakers", []):
bookmaker = book["bookmaker"]
markets = book.get("markets", {})
match_odds = markets.get("match_odds")
if not match_odds:
continue
print(bookmaker, match_odds)
Use this for odds comparison, model validation, and pre-match pricing displays.
Poll Live Odds During a Match
Live odds are available for supported fixtures. Polling frequency should match your product and plan limits.
import time
from football_api import get
match_id = "mt_010249745"
while True:
live_odds = get(f"/football/matches/{match_id}/odds/live")["data"]
print("Live odds snapshot")
for book in live_odds.get("bookmakers", []):
print(book["bookmaker"], book.get("markets", {}).keys())
time.sleep(30)
For production, write each snapshot to a database and stop polling when the match is no longer live.
Build a Simple Premier League Table Script
The team stats endpoint includes position, points, matches played, goals, and form for a team in a season. To build a table, fetch the teams in a competition, then fetch each team's season stats.
from football_api import get
competition_id = "comp_3039"
season_id = "sn_7598"
teams = get(
"/football/teams",
{"competition_id": competition_id, "season_id": season_id, "per_page": 20},
)["data"]
rows = []
for team in teams:
stats = get(
f"/football/teams/{team['id']}/stats",
{"season_id": season_id},
)["data"]
rows.append({**stats, "team_name": team["name"]})
for row in sorted(rows, key=lambda item: item["position"]):
print(
f"{row['position']:>2}. {row['team_name']:<24} "
f"{row['matches_played']:>2} {row['points']:>3} pts "
f"{row['goal_difference']:>4} GD"
)
Advanced Example: xG Form Over Last 10 Matches
This script fetches a team's last 10 finished matches and calculates average xG difference.
from statistics import mean
from football_api import get
team_id = "tm_9145"
matches = get(
"/football/matches",
{"team_id": team_id, "status": "finished", "per_page": 10},
)["data"]
xg_diffs = []
for match in matches:
if not match.get("xg_available"):
continue
stats = get(f"/football/matches/{match['id']}/stats")["data"]
xg = stats["overview"]["expected_goals"]["all"]
is_home = match["home_team"]["id"] == team_id
team_xg = xg["home"] if is_home else xg["away"]
opponent_xg = xg["away"] if is_home else xg["home"]
xg_diffs.append(team_xg - opponent_xg)
if xg_diffs:
print(f"Average xG diff: {mean(xg_diffs):.2f}")
else:
print("No xG data found in sample.")
This is a basic predictive feature. Combine it with rest days, injuries, odds movement, and opponent strength for a more serious model.
Production Tips
- Store your API key in environment variables, not source code.
- Cache API responses in your database or Redis.
- Keep long-running sync jobs separate from user requests.
- Use pagination for backfills.
- Check availability flags such as
xg_availableandlive_odds_available. - Log failed requests with endpoint, params, and response body.
- Avoid high-frequency polling unless your plan supports it.
FAQ
Can I use Python with TheStatsAPI?
Yes. TheStatsAPI is a REST JSON API, so any Python HTTP client works. The examples above use requests.
Do I need an SDK?
No. You can use plain HTTP requests. That keeps your integration simple and portable across scripts, notebooks, and backend services.
Can I fetch football odds with Python?
Yes. Use /football/matches/{match_id}/odds for pre-match odds and /football/matches/{match_id}/odds/live for live odds where available.
Can I get xG with Python?
Yes. Use /football/matches/{match_id}/stats for team xG, /football/matches/{match_id}/player-stats for player xG and xA, and /football/matches/{match_id}/shotmap for shot-level xG.
Ready to Power Your Sports App?
Start your 7-day free trial. All endpoints included on every plan.