machina-sports/sports-skills

football-data

Football (soccer) data across the world's major leagues — standings, schedules, match stats, xG, transfers, player profiles, head-to-head history, team strength (Elo), and match forecasts.

Voir la source
Document Skill original

Rendu depuis le dépôt source en conservant titres, exemples, code, tableaux, liens et images.

Football Data

Before writing queries, consult references/api-reference.md for endpoints, ID conventions, and data shapes.

Setup

Before first use, check if the CLI is available:

bash
which sports-skills || pip install sports-skills

If pip install fails (package not found or Python version error), install from GitHub:

bash
pip install git+https://github.com/machina-sports/sports-skills.git

The package requires Python 3.10+. If your default Python is older, use a specific version:

bash
python3 --version  # check version
# If < 3.10, try: python3.12 -m pip install sports-skills
# On macOS with Homebrew: /opt/homebrew/bin/python3.12 -m pip install sports-skills

No API keys required.

Quick Start

Prefer the CLI — it avoids Python import path issues:

bash
sports-skills football get_daily_schedule
sports-skills football get_season_standings --season_id=premier-league-2025

Python SDK (alternative):

python
from sports_skills import football

standings = football.get_season_standings(season_id="premier-league-2025")
schedule = football.get_daily_schedule()

CRITICAL: Before Any Query

CRITICAL: Before calling any data endpoint, verify:

  • Season ID is derived from get_current_season(competition_id="...") — never hardcoded.
  • Team ID is resolved via search_team(query="...") and passed as the numeric team_id. For get_head_to_head, get_team_strength, and get_match_forecast, always pass IDs — ambiguous names (e.g. two "Paris" clubs) can resolve to the wrong team.
  • The endpoint actually covers the league in question — see the Coverage & Source Map below. Coverage is uneven across sources; an uncovered call returns an empty payload with a message, not data.
  • get_event_xg and get_event_players_statistics (with xG) are only called for top-5 leagues (EPL, La Liga, Bundesliga, Serie A, Ligue 1).
  • get_season_leaders and get_missing_players are only called for Premier League seasons (season_id must start with premier-league-).

Choosing the Season

Derive the current year from the system prompt's date (e.g., currentDate: 2026-02-16 → current year is 2026).

  • If the user specifies a season, use it as-is.
  • If the user says "current", "latest", or doesn't specify: Call get_current_season(competition_id="...") to get the active season_id. Do NOT guess or hardcode the year.
  • Season format: Always {league-slug}-{year} (e.g., "premier-league-2025" for the 2025-26 season). The year is the start year of the season, not the end year.
  • MLS exception: MLS runs spring-fall within a single calendar year. Use get_current_season(competition_id="mls").

Coverage & Source Map

This skill stitches several free sources together. Coverage is not uniform — each endpoint works only where its underlying source has data. Check this before promising an answer; when an endpoint isn't covered, it returns an empty payload with an explanatory message (never an error) — read that message and fall back.

Endpoint(s)SourceCoverage
standings, schedules, teams, event summary/lineups/stats/timelineESPNAll leagues (broadest — the backbone)
get_event_xg, get_event_players_statistics (xG fields)UnderstatTop 5 only (EPL, La Liga, Bundesliga, Serie A, Ligue 1). Not RFPL — Understat dropped it.
get_season_leaders, get_missing_playersFPLPremier League only
get_player_profile, get_season_transfers (market value)TransfermarktAny player with a tm_player_id
get_head_to_headfootball-data.co.uk11 European domestic leagues (EPL, Championship, La Liga, Serie A, Bundesliga, Ligue 1, Eredivisie, Primeira Liga, Scottish, Belgian, Turkish). Same-division meetings only.
get_team_strengthClubElo, falling back to local EloEuropean clubs (incl. Russia). Falls back to ratings computed from football-data.co.uk when ClubElo is down.
get_match_forecastClubEloEuropean clubs (incl. Russia). No fallback — needs ClubElo's fixture feed.

Rule of thumb: ESPN answers "what happened" everywhere; the enrichment sources ( Understat/FPL/ClubElo/football-data.co.uk ) add depth only in their coverage zone. ESPN is always the fixture/score authority — never let an enrichment source override an ESPN score.

Gotchas (from live testing)

  • `get_team_profile` returns the squad. data.players[] carries the current roster with ESPN athlete ids, shirt numbers and ages — use it instead of collecting names match by match.
  • `get_player_season_stats` takes the same league slug as everything else (serie-a-brazil, not only ESPN's bra.1), and its gamelog is the last ~5 matches across competitions, not a season total.
  • Scored penalties are `penalty_goal` in the timeline. Count goal + penalty_goal + own_goal when reconciling with the score.
  • Pass IDs, not ambiguous names. For H2H/strength/forecast, resolve teams with search_team first and pass the numeric team_id. Names like "Paris Saint-Germain" can collapse onto the wrong club (Paris FC) during name resolution.
  • ClubElo off-season gaps: current-date get_team_strength can miss clubs in the summer break (a club's weekly Elo period may not span today). If a well-known club returns unresolved, pass an in-season date (e.g. date="2026-03-01").
  • ClubElo outages: get_team_strength falls back to locally computed Elo and sets source: "local-elo". Check that field before comparing numbers across calls — the local scale is division-local, so a rating means nothing outside its own division and cross-division comparisons are refused. The fallback honours date (it rates the division as of that date, and each entry's as_of is the last match counted). get_match_forecast has no fallback and stays empty.
  • `get_match_forecast` is short-horizon: ClubElo only forecasts ~a week ahead — empty between matchdays / off-season. That's expected, not a failure.
  • H2H is same-division only: two clubs that met in a cup or across tiers won't show; it counts league meetings in the resolved division.
  • H2H tells "unresolved" apart from "never met": football-data.co.uk uses short exonyms/abbreviations ("FC Koln", "M'gladbach", "Sp Lisbon"). Each club in teams[] reports resolved + matched_as; if a club is resolved: false, zero meetings means the lookup failed, not that the clubs never played.

Combining Endpoints (mix-and-match)

Compose sources for richer answers. Run independent calls in parallel.

  • Match preview (X vs Y): search_team ×2 → get_head_to_head (recent record) + get_team_strength(team_id, team_id_2) (Elo gap / favorite) + get_match_forecast (if within ~a week: W/D/L + scoreline). For a top-5 fixture add historical get_event_xg context from recent meetings.
  • Match report (post-game): get_event_summary + get_event_statistics + get_event_timeline, and for top-5 leagues get_event_xg + get_event_players_statistics.
  • Team form + context: get_team_schedule (recent results) + get_team_strength (current Elo & rank) + get_missing_players (PL only) + per-match get_event_xg (top-5).
  • Rivalry / derby deep dive: get_head_to_head (all-time-ish record + goals) + get_team_strength comparison for the current power balance.
  • Odds sanity-check: get_match_forecast gives a free model baseline (W/D/L) to compare against the kalshi / polymarket betting skills.

When a piece of the composition isn't covered (e.g. xG outside the top 5, H2H for MLS), skip it silently and deliver the parts that are covered — don't block the whole answer on one missing source.

Commands

CommandDescription
get_current_seasonDetect current season for a competition
get_competitionsList available competitions with current season info
get_competition_seasonsAvailable seasons for a competition
get_season_scheduleFull season match schedule
get_season_standingsLeague table for a season
get_season_leadersTop scorers/leaders (Premier League only)
get_season_teamsTeams in a season
search_teamSearch for a team by name
search_playerSearch for a player by name
get_team_profileTeam info + current squad (roster)
get_daily_scheduleAll matches for a date across all leagues
get_event_summaryMatch summary with scores
get_event_lineupsMatch lineups
get_event_statisticsMatch team statistics
get_event_timelineMatch timeline (goals, cards, subs)
get_team_scheduleSchedule for a specific team
get_head_to_headHistorical H2H results + stats (European domestic leagues)
get_team_strengthElo rating / two-team comparison (European clubs); local-Elo fallback if ClubElo is down
get_match_forecastClubElo win/draw/loss + scoreline forecast (~week ahead)
get_event_xgxG data (top 5 leagues only)
get_event_players_statisticsPlayer-level match stats with optional xG
get_missing_playersInjured/doubtful players (Premier League only)
get_season_transfersTransfer history via Transfermarkt
get_player_season_statsPlayer season stats via ESPN
get_player_profilePlayer profile (FPL and/or Transfermarkt)

See references/api-reference.md for full parameter lists, return shapes, and data coverage table.

Examples

Example 1: Premier League table User says: "Show me the Premier League table" Actions:

  1. Call get_current_season(competition_id="premier-league") to get the current season_id
  2. Call get_season_standings(season_id=<season_id from step 1>)

Result: Standings table with position, team, played, won, drawn, lost, GD, points

Example 2: Match report User says: "How did Arsenal vs Liverpool go?" Actions:

  1. Call get_daily_schedule() or get_team_schedule(team_id="359") to find the event_id
  2. Call get_event_summary(event_id="...") for the score
  3. Call get_event_statistics(event_id="...") for possession, shots, etc.
  4. Call get_event_xg(event_id="...") for xG comparison (EPL — top 5 only)

Result: Match report with scores, key stats, and xG

Example 3: Team deep dive User says: "Deep dive on Chelsea's recent form" Actions:

  1. Call search_team(query="Chelsea") → team_id=363, competition=premier-league
  2. Call get_team_schedule(team_id="363", competition_id="premier-league") → find recent closed events
  3. For each recent match, call in parallel: get_event_xg, get_event_statistics, get_event_players_statistics
  4. Call get_missing_players(season_id=<season_id>) → filter Chelsea's injured/doubtful players

Result: xG trend across matches, key player stats, and injury report

Example 4: Player market value User says: "What's Saka's market value?" Actions:

  1. Call get_player_profile(tm_player_id="433177") for Transfermarkt data
  2. Optionally add fpl_id for FPL stats

Result: Market value, value history, and transfer history

Example 5: Non-PL club User says: "Tell me about Corinthians" Actions:

  1. Call search_team(query="Corinthians") → team_id=874, competition=serie-a-brazil
  2. Call get_team_schedule(team_id="874", competition_id="serie-a-brazil") for fixtures
  3. Pick a recent match and call get_event_timeline(event_id="...") for goals, cards, subs

Result: Fixtures, timeline events (note: xG, FPL stats, and season leaders NOT available for Brazilian Serie A)

Example 6: Match preview (mix-and-match) User says: "Preview Arsenal vs Man City this weekend" Actions:

  1. Call search_team(query="Arsenal") and search_team(query="Manchester City") → team_ids 359, 382
  2. In parallel: get_head_to_head(team_id="359", team_id_2="382") (recent record + goals),

get_team_strength(team_id="359", team_id_2="382") (Elo gap + favorite), get_match_forecast(team_id="359", team_id_2="382") (W/D/L + likely scoreline, if within ~a week)

  1. Synthesize: form/record + power balance + model odds. Skip any piece that returns empty (e.g. forecast if the match is >1 week out).

Result: A preview blending head-to-head history, current strength, and a free model forecast

Commands that DO NOT exist — never call these

  • ~~get_standings~~ — the correct command is get_season_standings (requires season_id).
  • ~~get_live_scores~~ — not available. Use get_daily_schedule() for today's matches.
  • ~~get_team_squad~~ / ~~get_team_roster~~ — use get_team_profile: data.players[] is the current roster with ESPN athlete ids (see Gotchas). get_season_leaders + get_player_profile remain the path for career data.
  • ~~get_transfers~~ — the correct command is get_season_transfers (requires season_id + tm_player_ids).
  • ~~get_match_results~~ / ~~get_match~~ — use get_event_summary with an event_id.
  • ~~get_player_stats~~ — use get_event_players_statistics for match-level stats, or get_player_profile for career data.
  • ~~get_scores~~ / ~~get_results~~ — use get_event_summary with an event_id.
  • ~~get_fixtures~~ — use get_daily_schedule for today's matches or get_season_schedule for a full season.
  • ~~get_league_table~~ — use get_season_standings with a season_id.

If a command is not in the Commands table above, it does not exist. Do not try commands not listed.

Error Handling

When a command fails (wrong event_id, missing data, network error, etc.), do not surface the raw error to the user. Instead:

  1. Catch it silently — treat the failure as an exploratory miss.
  2. Try alternatives — if an eventid returns no data, call `getdailyschedule()` or `getteam_schedule()` to discover the correct ID.
  3. Only report failure after exhausting alternatives — use a clean message (e.g., "I couldn't find that match — can you confirm the teams or date?").

Troubleshooting

Error: sports-skills command not found Cause: Package not installed Solution: Run pip install sports-skills. If not on PyPI, install from GitHub: pip install git+https://github.com/machina-sports/sports-skills.git

Error: ModuleNotFoundError: No module named 'sports_skills' Cause: Package not installed or path issue Solution: Install the package. Prefer the CLI over Python imports to avoid path issues

Error: get_season_leaders or get_missing_players returns empty for a non-PL league Cause: These commands only work for Premier League; they silently return empty for other leagues Solution: Check the Data Coverage table in references/api-reference.md. For other leagues, use get_event_players_statistics for player data

Error: get_team_profile returns no players Cause: ESPN has no roster for that team id in the given league (wrong league_slug, or a national team / youth side) Solution: Pass the league_slug the club plays in (e.g. serie-a-brazil); for match-day squads use get_event_lineups

Error: Wrong seasonid format Cause: Season ID must follow the `{league-slug}-{year}` format Solution: Use `getcurrentseason(competitionid="...") to discover the correct format. Example: "premier-league-2025", not "2025-2026" or "EPL-2025"`

Error: No xG data for a recent match Cause: Understat data may lag 24-48 hours after a match ends Solution: If get_event_xg returns empty for a recent top-5 match, retry later. Only available for EPL, La Liga, Bundesliga, Serie A, Ligue 1

Error: Team or event ID unknown Cause: ID was guessed instead of looked up Solution: Use search_team(query="team name") to find team IDs, or get_daily_schedule / get_season_schedule to find event IDs. Never guess IDs.