TUTORIAL

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.

5 min read

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_available and live_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.

Start building today

Ready to Power Your Sports App?

Start your 7-day free trial. All endpoints included on every plan.

Cancel anytime
7-day free trial
Setup in 5 minutes