Overview
Load real open football data in one call — and when not to bother.
@pitchkit/data-providers takes you from a match id to chart-ready data without a pipeline in
between. It's a separate, optional package with no dependency on @pitchkit/core or
@pitchkit/react — its only runtime dependency is csv-parse, for SkillCorner's CSV files.
npm install @pitchkit/data-providersimport { fetchMatchEvents, shots, isGoal } from "@pitchkit/data-providers/statsbomb";
import { Scatter, VerticalPitch } from "@pitchkit/react";
const events = await fetchMatchEvents(3943043); // Spain 2–1 England, Euro 2024 final
const spain = shots(events).filter((shot) => shot.team.name === "Spain");
<VerticalPitch type="statsbomb">
<Scatter
data={spain}
x={(s) => s.x}
y={(s) => s.y}
fill={(s) => (isGoal(s) ? "orange" : "steelblue")}
/>
</VerticalPitch>;That's the whole path. No adapter, no field mapping, no coordinate conversion.
You probably don't need this
PitchKit's layers take accessor functions, so they already read whatever shape your data is
in — that's the point of the coordinates guide. If you have your
own pipeline, point x/y at your own fields and skip this package entirely. Nothing in
@pitchkit/react depends on it.
It exists for the cases where fetching real data is the friction: tutorials, prototypes, learning the API, and agent-assisted builds where a complete runnable example beats a schema description.
How the loaders are shaped
Four layers, each usable on its own:
| Layer | What it does |
|---|---|
parse* | Pure — already-loaded JSON in, typed objects out. No network. |
load* / fetch* | Fetch and parse in one call. load* takes any URL; fetch* builds the open-data URL for you. |
| Selectors | Narrow a mixed feed: shots(), passes(), carries(), ofType(). |
| Predicates | Compose with .filter(): isGoal, isComplete, isCorner, isCross, … |
Two principles run through all of it, and they're worth knowing before you read the provider pages:
The provider's data stays the provider's. Field names keep their original spelling, values
keep their original strings — a StatsBomb outcome is "Off T", not a re-spelled
"off-target". You can read StatsBomb's own spec alongside these types with no mapping table
in between. The one exception is coordinates: location arrays are also surfaced as
x/y/endX/endY, because that's what a PitchKit accessor wants.
Interpretation lives in functions, not fields. There's no invented complete: boolean on a
pass — there's an isComplete(pass) you apply. The data stays a faithful record; the reading
of it is opt-in and inspectable. It also covers what a per-event-type accessor structurally
can't: a corner isn't a StatsBomb event type, it's a kind of pass, so it can only be a
predicate.
Providers
- StatsBomb — open data: competitions, matches, lineups,
events and 360 tracking.
- Source: statsbomb/open-data · specifications · free data hub
- SkillCorner — broadcast tracking at 10 fps, plus the
derived dynamic events and
phases of play.
- Source: SkillCorner/opendata · documentation · skillcorner.com
- Wyscout — the Pappalardo et al. dataset: 1,941 matches of
event data across the 2017/18 big-five leagues, plus World Cup 2018 and Euro 2016.
- Source: figshare collection · data paper · event mirror
SkillCorner also has its own pitch type. Its coordinates are metres from the centre spot, so
<Pitch type="skillcorner"> plots them raw — no lifting, no conversion:
<Pitch type="skillcorner" dimensions={{ length: match.pitch_length, width: match.pitch_width }}>
<Scatter data={frame.player_data} x={(p) => p.x} y={(p) => p.y} />
</Pitch>Wyscout has its own pitch type too — a 0–100 percentage grid like Opta's, but with a
different origin: top-left with y increasing downward, where Opta's is bottom-left with y
increasing upward. <Pitch type="wyscout"> plots its coordinates raw:
<Pitch type="wyscout">
<Scatter data={shots} x={(s) => s.x} y={(s) => s.y} />
</Pitch>Licensing
This package ships no data — it fetches from whatever URL you give it. The default URLs point at each provider's own open-data repository, and every provider asks to be credited in anything you publish from their data:
- StatsBomb — open-data is released under StatsBomb's own user agreement rather than an OSI licence. Read the usage terms before you rely on it.
- SkillCorner — opendata is MIT-licensed, and SkillCorner ask for credit and a mention when you publish work built on it.
- Wyscout — the Pappalardo et al. dataset is CC BY 4.0, the most permissive of the three; cite the paper in anything you publish. Its official release (figshare) has no per-match JSON file, so events are fetched from a community mirror that splits the same archive per match with no field renamed — the reference files (competitions, teams, players) come from figshare directly.