The Python client

Standard library only, and it goes straight into pandas.

An official Python client is published at github.com/FootballSoccerAPI/footballsoccerapi-python. Standard library only — no requests, no httpx, nothing to keep in step with anything else.

That is deliberate. A data client that drags in a stack is a client that breaks when the stack moves, and the people using this already have enough of that.

There are two versions in it, and the smaller one is the better place to start.

The one-file version

Copy fsapi_simple.py next to your script or notebook. Three functions, no classes, nothing to install.

from fsapi_simple import fsapi_get

matches = fsapi_get("/v1/matches",
                       country="England",
                       status="finished",
                       limit=5)

for m in matches:
    print(m["home_team_name"], m["home_goals"],
          m["away_goals"], m["away_team_name"])

fsapi_get() hands back the data. fsapi_call() hands back the data and the meta. fsapi_walk() pages through a whole set for you. That is all of it, and the names match the PHP client exactly, so moving between them is not a relearn.

Straight into pandas

The client does not import pandas and does not depend on it. It does not need to — a response is plain dictionaries, so it goes into a DataFrame with no adapter and no helper.

import pandas as pd
from footballsoccerapi import Client

api = Client()                 # reads FSAPI_KEY

rows = list(api.walk_matches(league_id="lg_24T9Z0G", season=2024))
df = pd.DataFrame(rows)

df["total_goals"] = df["home_goals"] + df["away_goals"]
df.groupby("league_name")["total_goals"].mean()

Check coverage before trusting an average. A field we do not hold comes back as null rather than vanishing, so the column is always there and pandas reads it as NaN. Counting those tells you what an average is actually over — prices sit at 53.7% of the archive and the share varies a lot by competition.

df["kickoff_book_pct"].notna().sum()   # how many actually had a price

Your key

Pass it directly, or leave it out and let the client read FSAPI_KEY from the environment:

api = Client("fsa_live_...")   # directly
api = Client()                   # reads FSAPI_KEY
FSAPI_KEY=fsa_live_... python your-script.py

The environment version matters more in Python than most places, because notebooks get shared. A key written into a cell ends up in a screenshot, a repository or a colleague’s copy eventually, and then it has to be rotated.

The package

Same data, more handled for you: typed exceptions, cursor walking, and batching that chunks a long list rather than refusing it.

pip install git+https://github.com/FootballSoccerAPI/footballsoccerapi-python.git
from footballsoccerapi import Client

api = Client()
res = api.matches(season=2024)

res["data"]                 # the matches
res["meta"]["total"]        # how many matched
res["meta"]["data_as_of"]   # when the archive was rebuilt

The meta comes back with the data rather than being stripped, because it is where the API says how old the archive is and how many rows there were — the part that stops a figure being quoted without its base.

What the exceptions tell you

from footballsoccerapi import PlanRequiredError, RateLimitError

try:
    api.live()
except PlanRequiredError as e:
    print(f"Needs {e.needs_plan}, you have {e.your_plan}")
except RateLimitError as e:
    time.sleep(e.retry_after)

Rate limits are retried once by default using the API’s own figure, because guessing a backoff when the response tells you the answer is worse for both sides. A reply that is not JSON raises TransportError saying so, which means something between you and the API answered — a proxy, or a challenge page — and knowing which layer failed saves an hour.

Walking a season

for match in api.walk_matches(league_id="lg_24T9Z0G", season=2024):
    ...   # every match, memory flat

The cursor pages by sort position rather than offset, so the ten-thousandth page is as quick as the first and a match arriving mid-walk cannot shift the boundary and make you skip a row.

The examples

export FSAPI_KEY=your_key

python examples/simple.py
python examples/quickstart.py
python examples/walk_a_season.py
python examples/into_pandas.py

MIT licensed. If something is wrong or missing, an issue on the repository or a ticket both reach us.