Skip to content

football-data.org Source Research

Status: implemented in pkg/source/football_data_org with supported-source docs at docs/supported-sources/football-data-org.md.

Primary docs:

Connection

ItemValue
Base URLhttps://api.football-data.org/v4
AuthX-Auth-Token: <api-token> request header
World Cup identifierCompetition code WC; competition ID is 2000 in current examples
Response shapeResource-specific JSON objects, commonly with filters, resultSet, and resource arrays such as matches, teams, or standings.
ThrottlingFree registered clients are 10 requests/minute; paid plans increase the per-minute quota.
Deep match fieldsControlled by headers: X-Unfold-Lineups, X-Unfold-Bookings, X-Unfold-Subs, and X-Unfold-Goals.

World Cup appears in the free tier coverage list. Deep data such as lineups, substitutions, cards, goal scorers, and squads is paid-plan territory according to the pricing page.

Tier Map

PlanMonthly priceRate limitRelevant coverage
Anonymous$0100 requests/24hArea and competition list resources only.
Free€010/min12 competitions including Worldcup; delayed scores/schedules, fixtures, league tables.
Free w/ Livescores€1220/minSame 12 competitions with live scores.
ML Pack Light€2920/minAdds advanced trend/form data and 10 seasons of history.
Free + Deep Data€2930/minAdds lineups, substitutions, goal scorers, bookings/cards, and squads.
Standard€4960/min30 competitions plus deep data.
Advanced€99100/min50 competitions plus deep data.
Pro€199120/min100 competitions plus deep data.
Odds add-on€15N/APre-match home/draw/away odds; requires a regular plan.
Statistic add-on€15N/ACorners, free kicks, goal kicks, offsides, fouls, possession, saves, throw-ins, shots, cards; requires a regular plan.

Main Table Coverage

Main tableCoveragePrimary endpointRequired tierNotes
TeamsYesGET /competitions/WC/teams?season=2026Free for basic team list; deep data for squadsUse /teams/{id} to hydrate a team and squad when plan allows.
StadiumsPartialMatch/team venue string fieldsFree for basic stringsNo dedicated stadium resource with capacity/geo fields. Treat as a derived table from matches and teams.
Group standingsYesGET /competitions/WC/standings?season=2026Free for league tablesStandings behavior differs by competition type; emitted as one row per team per standing block, keyed by stage, type, and group.
MatchesYesGET /competitions/WC/matches?season=2026Free for fixtures/scores; live/deep fields need paid planUse unfold headers for goals, bookings, substitutions, and lineups.
PlayersPartial/teams/{id} squad, /persons/{id}, /competitions/WC/scorersDeep data for squads/scorersThere is no league-wide players endpoint equivalent to API-Football.
Match eventsPartialUnfolded match fields via X-Unfold-* headersDeep data planEvents are separate arrays for goals, bookings, substitutions, and lineups rather than one timeline endpoint.

Endpoint Map

Competitions

GET /competitions

Use to discover accessible competitions and confirm WC availability for the authenticated client.

ParameterRequiredTypeUse
areasNocomma-separated area IDsFilter competition list by area.

GET /competitions/{id}

Use id as either numeric ID or code such as WC.

ParameterRequiredTypeUse
Path {id}Yesinteger or string codeUse WC for FIFA World Cup.

The competition response includes available seasons, current season metadata, winner when known, and competition plan.

Teams

GET /competitions/{id}/teams

ParameterRequiredTypeUse
Path {id}Yesinteger or string codeUse WC.
seasonNoYYYY integerUse 2026 for World Cup 2026.

Recommended World Cup call:

http
GET /competitions/WC/teams?season=2026
X-Auth-Token: <api-token>

GET /teams/{id}

ParameterRequiredTypeUse
Path {id}YesintegerHydrate one team.

Team detail includes base fields such as id, name, shortName, tla, crest, address, website, founded, clubColors, venue, running competitions, coach, squad, staff, and lastUpdated when available to the plan.

Stadiums

football-data.org does not expose a first-class stadium/venue endpoint. Build a derived stadiums table by extracting and deduplicating:

  • matches[].venue from /competitions/WC/matches.
  • team.venue from /competitions/WC/teams or /teams/{id}.

Implemented columns: venue_key (sha1 of the normalized venue name, primary key), venue, source_context (team or match), team_id (nullable), match_id (nullable), and raw (the originating team/match object kept as JSON).

Group Standings

GET /competitions/{id}/standings

ParameterRequiredTypeUse
Path {id}Yesinteger or string codeUse WC.
seasonNoYYYY integerUse 2026.
matchdayNointegerSnapshot by matchday.
dateNoYYYY-MM-DDSnapshot by date.

Recommended World Cup call:

http
GET /competitions/WC/standings?season=2026

Emit one row per team per standing block. The composite key fields (competition_id, season_id, stage, standing_type, group_name, team_id) are surfaced as top-level columns; the full table row is kept under a standing JSON column, alongside competition and season. Fields are not flattened — schema inference types the scalars and preserves nested objects as JSON.

Matches

GET /competitions/{id}/matches

ParameterRequiredTypeUse
Path {id}Yesinteger or string codeUse WC.
seasonNoYYYY integerUse 2026.
matchdayNointegerMatchday filter.
statusNostatus enumExample: SCHEDULED, TIMED, IN_PLAY, PAUSED, FINISHED, LIVE.
dateFromNoYYYY-MM-DDInclusive start date.
dateToNoYYYY-MM-DDExclusive end date in v4 behavior.
stageNostage enumExample: GROUP_STAGE, LAST_32, LAST_16, QUARTER_FINALS, SEMI_FINALS, THIRD_PLACE, FINAL.
groupNogroup enumGROUP_A through GROUP_L.

Recommended World Cup call:

http
GET /competitions/WC/matches?season=2026

Optional deep-data headers:

HeaderUse
X-Unfold-Lineups: trueInclude lineups in match list responses.
X-Unfold-Bookings: trueInclude cards/bookings.
X-Unfold-Subs: trueInclude substitutions.
X-Unfold-Goals: trueInclude goals.

GET /matches

Cross-competition match list.

ParameterRequiredTypeUse
competitionsNocomma-separated IDs/codesUse WC when querying across competitions.
idsNocomma-separated match IDsHydrate selected matches.
dateNoYYYY-MM-DDMatches on one date.
dateFromNoYYYY-MM-DDStart date, with dateTo.
dateToNoYYYY-MM-DDEnd date.
statusNostatus enumFilter by match status.

GET /matches/{id}

Fetch one match by ID. Use when detail refresh is cheaper or clearer than list refresh.

Players

football-data.org models players as Person resources and squad members nested under teams/matches.

GET /persons/{id}

ParameterRequiredTypeUse
Path {id}YesintegerHydrate one player/person.

GET /persons/{id}/matches

ParameterRequiredTypeUse
Path {id}YesintegerPerson ID.
dateFromNoYYYY-MM-DDStart date.
dateToNoYYYY-MM-DDEnd date.
statusNostatus enumFilter by match status.
competitionsNocomma-separated IDs/codesUse WC for World Cup.
limitNointegerResult limit.
offsetNointegerPagination offset.

GET /competitions/{id}/scorers

Use this as a limited player-stat source for scorers/assists.

ParameterRequiredTypeUse
Path {id}Yesinteger or codeUse WC.
seasonNoYYYY integerUse 2026.
matchdayNointegerCompare scorer lists at a matchday.

The implemented players table starts from /competitions/WC/teams?season=2026 to enumerate team IDs, then hydrates each /teams/{id} for the richer squad. Each squad member becomes one row keyed by (team_id, id), with the raw player object kept as a player JSON column (not flattened). /teams/{id} requires plan access, so on the free plan this errors; the basic embedded squad is available on the teams table's squad column instead.

Match Events

There is no single /events endpoint. Build match_events from unfolded match arrays:

Event familyRequest headerExpected fields
GoalsX-Unfold-Goals: trueminute, injury time, type, team, scorer, assist, score.
Bookings/cardsX-Unfold-Bookings: trueminute, team, player, card type.
SubstitutionsX-Unfold-Subs: trueminute, team, player out/in.
LineupsX-Unfold-Lineups: truestarting/bench players and team formations.

Recommended ingestion flow:

  1. Fetch /competitions/WC/matches?season=2026 with unfold headers enabled when the key has deep-data access.
  2. Create one normalized event row per item in goals, bookings, and substitutions.
  3. Store lineups separately or use them to enrich player participation, because they are not chronological events.

Implementation Notes (as shipped)

  • URI: footballdata://?api_key=<token>&competition=WC&season=2026. Auth via the X-Auth-Token header, not a query parameter. base_url is supported for tests.
  • Default competition is WC; default season is 2026.
  • Schema is inferred (KnownSchema: false) — nested provider objects are preserved as JSON columns and scalar fields are typed automatically. Fields are not flattened into per-attribute columns.
  • Strategy per table: merge where the table accepts a time filter or returns lastUpdated (teams, matches, match_events); replace otherwise (stadiums, group_standings, players).
  • HandlesIncrementality() is true. matches and match_events pass --interval-start/--interval-end to the API as server-side dateFrom/dateTo. Replace tables ignore the interval and always full-fetch.
  • The HTTP client is rate-limited to ~80% of the free tier's 10 requests/minute, and responses are decoded with json.Number to preserve large-integer precision.
  • Stadiums and match events are derived tables because the API has no first-class stadium or unified event resource; players is derived from team squads.
  • Deep player/event fields are subscription-dependent: match_events loads 0 rows without the Deep Data plan, and players requires /teams/{id} access.