Library / SDK
hudl/statsbombpy avatar
hudl/statsbombpy

statsbombpy: streaming football event data into a pandas workflow

Easily stream StatsBomb data into Python

747 stars105 forksPythonLicense varies

At a glance

What is it?
statsbombpy is a small Python package from StatsBomb that pulls competitions, matches, events and frames into pandas, using your API credentials or the free open data. The paid API and the open data sit behind the same interface, and the data itself comes with attribution terms.
Who is it for?
statsbombpy suits an analyst who wants football event data in pandas without hand writing API plumbing, and who can work with either a paying StatsBomb account or the open competitions. It is the wrong tool for a project needing a specific match the open data does not cover, since API access is for paying customers only, and for anyone unwilling to meet the attribution terms.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A thin client between pandas and the StatsBomb API

statsbombpy is a Python package for streaming StatsBomb data into Python, and it is deliberately thin. The description in the package metadata is a sentence long, and the dependency list is short: pandas for the tabular results, requests and requests-cache for talking to the API without refetching, joblib for the parallel work, and inflect, which handles the pluralisation of event type names that the data uses.

The audience is football analytics, and more precisely the people who want to be inside the data rather than looking at a picture of it. Every function returns a pandas DataFrame, so the object you get back is something you can filter, join and model. The alternative, downloading JSON files and writing a parser, is not hard, but it is work you repeat for every new question.

There is a commercial boundary in the middle of this. The README states that API access is for paying customers only, and that the package can also be used with free data from the StatsBomb GitHub page. That split shapes the whole design, because the open competitions and the paid catalogue are reached through the same calls, so the same code can start on open data and move to the full catalogue when a subscription exists.

The project is published on PyPI as `statsbombpy`, and the package version in the setup script is 1.22.0.

Credentials come from the environment or from a dictionary

Authentication has two documented routes. The first is environment variables, `SB_USERNAME` and `SB_PASSWORD`, set to your login credentials for the API. The second is a `creds` argument that every function accepts, holding the credentials in the format `{"user": "", "passwd": ""}`.

The environment variable route is the one to reach for in production, because it keeps the password out of the source and out of notebooks that get shared. The dictionary form exists for the case where a script runs several identities in one process, or where a notebook wants to try one account and then another without restarting the kernel.

What the documentation does not describe is the failure behaviour, and that is a real gap for anyone automating this. Nothing states what happens when credentials are missing, when they are wrong, or when a session expires mid run, and there is no mention of token refresh or of rate limits and how a caller should back off. The absence is worth knowing about before the first long pull, because a long pull is exactly the situation where a session timeout would show up.

Support is routed through the Hudl support form, which is a small clue about the ownership picture: the package is authored by StatsBomb, and the support path sits with Hudl.

SB_CORES controls the parallel event and frame pulls

Two of the functions are slow enough that their concurrency is configurable. `sb.competition_events()` and `sb.competition_frames()` will use several of the machine's cores, and the number is controlled by the `SB_CORES` environment variable. Allowing the package more cores will speed those functions up, which is the stated reason the knob exists.

The default is worth knowing precisely, because it is not simply the core count. If `SB_CORES` is not set, the package tries to detect the number of cores in the system and uses two less than that number. If detection fails, it falls back to four.

The subtraction of two is a sensible heuristic, since a full saturation of the machine during a data pull leaves nothing for the rest of the stack, and the fallback of four is a conservative default for a container or a small virtual machine. But it is also a fixed rule with no awareness of memory pressure, and frames data is the heavier of the two calls, so a machine with many cores and little free memory can be pushed harder than the user intended.

This is a small design point that tells you something about the package. It is a convenience layer with sensible defaults rather than a scheduler, and for a one off analysis that is exactly the right amount of machinery.

The call sequence runs from competitions down to frames

The usage pattern is a walk down a hierarchy. The import brings in the `sb` object, which is the only interface a user needs.

python
from statsbombpy import sb

The first call lists everything available, competitions and their seasons.

python
sb.competitions()

The result is a DataFrame with one row per competition and season, with columns for the competition id, the season id, the country name, the competition name, the gender, the season name and the update and availability timestamps for the matches. The example output in the documentation shows German and Croatian leagues, which is a hint at what the open set contains.

The second call narrows to matches, and the arguments are the ids from the previous frame.

python
sb.matches(competition_id=9, season_id=42)

That result carries the match id, the date and kick off time, the competition and season names, the teams and the score, the match status, the last update timestamp, the match week, the competition stage, the stadium, the referee, the managers, and three version fields: the data version, the shot fidelity version and the xy fidelity version.

Those three version fields are the part experienced users care about most. Football event data changes when a provider corrects locations or shot models, and the fidelity versions are how a dataset records which capture pipeline produced it. The examples show values of 1.1.0, 2 and 2, so a join across matches with different values is a join across different data.

Publishing results carries attribution obligations

The data has its own terms, and they are stated in the README rather than being buried. StatsBomb asks users to register their details on the resource centre and to read the user agreement carefully, and the agreement itself is a document in the repository at `doc/LICENSE.pdf`. Using the repository is treated as agreeing to that user agreement.

The attribution requirement is concrete: if you publish, share or distribute research, analysis or insights based on the data, you are asked to state the data source as StatsBomb and to use their logo. That is a condition on the output, not a suggestion, and it applies to anything derived from the open data as well.

The README also asks for basic personal information from users of the data, in exchange for telling them when new data, tutorials and research are published, and it states that the information is stored in accordance with their privacy policy and the GDPR. The resource centre is where that registration happens.

For an academic or commercial project, this is the part of the deal to read first. A package that makes the data easy to fetch also makes it easy to publish something that does not credit the source, and the consequence falls on the user, not on the maintainers.

A small package with a clear release line

The package is deliberately conventional in its packaging. The setup script declares a single package directory, `packages=["statsbombpy"]`, reads the README as the long description, and lists the runtime dependencies with an extra group for development that contains nose2. The test command in the documentation is `nose2 -v --pretty-assert`, so the test suite is a plain unittest style suite run through nose2 rather than a pytest configuration, and the repository top level holds `statsbombpy/`, `tests/`, `doc/` and `setup.py` with no configuration file for a modern test runner or a lock file.

The release line moves quickly. The recent tags are v1.22.0 on 2026-07-30, v1.21.0 on 2026-07-09 and v1.20.0 on 2026-06-29, three minor versions in five weeks, and the version in the setup script matches the newest tag at 1.22.0. The last push was on 2026-10-19, so the repository is receiving commits after that release rather than sitting still.

Licensing is the murkiest part. GitHub reports the licence for the repository as one it cannot classify, while the data itself comes under the user agreement in `doc/LICENSE.pdf`. Those are two different things and they cover two different objects: the code that fetches the data, and the data that is fetched. Anyone planning to ship a product that uses this package needs to settle both questions separately.

Where the open data files differ from the API client

The alternative to this package is to skip it and work with the open data directly. The README states that StatsBomb's open data is accessible without authentication, and that certain leagues have been made freely available for public use for research projects. For a project confined to those competitions, the files themselves may be enough, and a plain pandas read of a directory of JSON is a defensible choice.

The difference in approach is what the API client adds. The open data is a snapshot per competition and season, so a researcher who needs a match that was released after the snapshot, or a corrected version of a match, has to wait or fetch again. The API path returns whatever the provider currently holds, with the update timestamps that tell you when it last changed.

The other difference is effort. With the API client, the identifier hierarchy, the parameter names and the credential handling are already written, and the same code works for open and paid competitions. With raw files, you write the loader, you decide the folder layout, and you handle the case where one competition's files use a different structure. For a single league, that is not worth a package. For a codebase that spans several seasons, several competitions and a need to refresh, the package earns its place.

The practical warning is the same either way. The open data is still governed by the same user agreement and the same attribution requirement, and the code that fetches it does not change the obligations that come with it.

Editorial conclusion

statsbombpy suits an analyst who wants football event data in pandas without hand writing API plumbing, and who can work with either a paying StatsBomb account or the open competitions. It is the wrong tool for a project needing a specific match the open data does not cover, since API access is for paying customers only, and for anyone unwilling to meet the attribution terms. Before you start, check which competitions are in the open set, register on the StatsBomb resource centre if you intend to publish results, and read doc/LICENSE.pdf, because using the repository means agreeing to the user agreement.

Frequently asked questions

How do I install and start using statsbombpy?

Install it with `pip install statsbombpy`, then import the client with `from statsbombpy import sb`. The first call is `sb.competitions()`, which returns one row per competition and season, and `sb.matches(competition_id=9, season_id=42)` narrows that to the matches of one season.

Do I need a StatsBomb account to use statsbombpy?

For the API, yes. API access is for paying customers only, and the credentials can be supplied through the SB_USERNAME and SB_PASSWORD environment variables or through the creds argument in the format {"user": "", "passwd": ""}. The open data can be accessed without authentication.

How do I control how many cores statsbombpy uses?

Set the SB_CORES environment variable to the number of cores you want `sb.competition_events()` and `sb.competition_frames()` to use. If it is not set, the package tries to detect the core count and uses two less than that, and if detection fails it uses four.

What are the terms for using StatsBomb open data?

The README asks users to register on the StatsBomb resource centre and to read the user agreement in doc/LICENSE.pdf, and it says that using the repository means agreeing to that agreement. If you publish research, analysis or insights based on the data, you are asked to state the data source as StatsBomb and to use their logo.

How do I run the statsbombpy test suite?

The documented command is `nose2 -v --pretty-assert`, and the development extra in the setup script contains nose2. The tests live in the tests directory of the repository, and the package ships a single package directory named statsbombpy.

Official sources

  1. hudl/statsbombpy on GitHub
  2. Issues
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hudl-statsbombpy.svg)](https://hysenlabs.com/projects/hudl-statsbombpy)

Was this useful?