SKILL.md
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:
which sports-skills || pip install sports-skills
If pip install fails (package not found or Python version error), install from GitHub:
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:
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:
sports-skills football get_daily_schedule
sports-skills football get_season_standings --season_id=premier-league-2025
Python SDK (alternative):
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
getcurrentseason(competition_id="...")— never hardcoded. - Team ID is resolved via
searchteam(query="...")and passed as the numericteamid. Forgetheadtohead,getteamstrength, andgetmatch_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. geteventxgandgeteventplayers_statistics(with xG) are only called for top-5 leagues (EPL, La Liga, Bundesliga, Serie A, Ligue 1).getseasonleadersandgetmissingplayersare only called for Premier League seasons (season_id must start withpremier-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
getcurrentseason(competitionid="...")to get the active seasonid. 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
getcurrentseason(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) | Source | Coverage |
|---|---|---|
| standings, schedules, teams, event summary/lineups/stats/timeline | ESPN | All leagues (broadest — the backbone) |
geteventxg, geteventplayers_statistics (xG fields) |
Understat | Top 5 only (EPL, La Liga, Bundesliga, Serie A, Ligue 1). Not RFPL — Understat dropped it. |
getseasonleaders, getmissingplayers |
FPL | Premier League only |
getplayerprofile, getseasontransfers (market value) |
Transfermarkt | Any player with a tmplayerid |
getheadto_head |
football-data.co.uk | 11 European domestic leagues (EPL, Championship, La Liga, Serie A, Bundesliga, Ligue 1, Eredivisie, Primeira Liga, Scottish, Belgian, Turkish). Same-division meetings only. |
getteamstrength, getmatchforecast |
ClubElo | European clubs (incl. Russia). |
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)
- Pass IDs, not ambiguous names. For H2H/strength/forecast, resolve teams with
searchteamfirst and pass the numericteamid. Names like "Paris Saint-Germain" can collapse onto the wrong club (Paris FC) during name resolution. - ClubElo off-season gaps: current-date
getteamstrengthcan 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-seasondate(e.g.date="2026-03-01"). getmatchforecastis 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[]reportsresolved+matched_as; if a club isresolved: 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):searchteam×2 →getheadtohead(recent record) +getteamstrength(teamid, teamid2)(Elo gap / favorite) +getmatchforecast(if within ~a week: W/D/L + scoreline). For a top-5 fixture add historicalgetevent_xgcontext from recent meetings. - Match report (post-game):
geteventsummary+geteventstatistics+geteventtimeline, and for top-5 leaguesgeteventxg+geteventplayers_statistics. - Team form + context:
getteamschedule(recent results) +getteamstrength(current Elo & rank) +getmissingplayers(PL only) + per-matchgeteventxg(top-5). - Rivalry / derby deep dive:
getheadtohead(all-time-ish record + goals) +getteam_strengthcomparison for the current power balance. - Odds sanity-check:
getmatchforecastgives a free model baseline (W/D/L) to compare against thekalshi/polymarketbetting 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
| Command | Description |
|---|---|
getcurrentseason |
Detect current season for a competition |
get_competitions |
List available competitions with current season info |
getcompetitionseasons |
Available seasons for a competition |
getseasonschedule |
Full season match schedule |
getseasonstandings |
League table for a season |
getseasonleaders |
Top scorers/leaders (Premier League only) |
getseasonteams |
Teams in a season |
search_team |
Search for a team by name |
search_player |
Search for a player by name |
getteamprofile |
Basic team info (no squad/roster) |
getdailyschedule |
All matches for a date across all leagues |
geteventsummary |
Match summary with scores |
geteventlineups |
Match lineups |
geteventstatistics |
Match team statistics |
geteventtimeline |
Match timeline (goals, cards, subs) |
getteamschedule |
Schedule for a specific team |
getheadto_head |
Historical H2H results + stats (European domestic leagues) |
getteamstrength |
ClubElo Elo rating / two-team comparison (European clubs) |
getmatchforecast |
ClubElo win/draw/loss + scoreline forecast (~week ahead) |
geteventxg |
xG data (top 5 leagues only) |
geteventplayers_statistics |
Player-level match stats with optional xG |
getmissingplayers |
Injured/doubtful players (Premier League only) |
getseasontransfers |
Transfer history via Transfermarkt |
getplayerseason_stats |
Player season stats via ESPN |
getplayerprofile |
Player 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:
- Call
getcurrentseason(competitionid="premier-league")to get the current seasonid - Call
getseasonstandings(seasonid=<seasonid 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:
- Call
getdailyschedule()orgetteamschedule(teamid="359")to find the eventid - Call
geteventsummary(event_id="...")for the score - Call
geteventstatistics(event_id="...")for possession, shots, etc. - Call
geteventxg(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:
- Call
searchteam(query="Chelsea")→ teamid=363, competition=premier-league - Call
getteamschedule(teamid="363", competitionid="premier-league")→ find recent closed events - For each recent match, call in parallel:
geteventxg,geteventstatistics,geteventplayers_statistics - Call
getmissingplayers(seasonid=<seasonid>)→ 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:
- Call
getplayerprofile(tmplayerid="433177")for Transfermarkt data - Optionally add
fpl_idfor FPL stats
Result: Market value, value history, and transfer history
Example 5: Non-PL club User says: "Tell me about Corinthians" Actions:
- Call
searchteam(query="Corinthians")→ teamid=874, competition=serie-a-brazil - Call
getteamschedule(teamid="874", competitionid="serie-a-brazil")for fixtures - Pick a recent match and call
geteventtimeline(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:
- Call
searchteam(query="Arsenal")andsearchteam(query="Manchester City")→ team_ids 359, 382 - In parallel:
getheadtohead(teamid="359", teamid2="382")(recent record + goals),
getteamstrength(teamid="359", teamid2="382") (Elo gap + favorite), getmatchforecast(teamid="359", teamid2="382") (W/D/L + likely scoreline, if within ~a week)
- 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
— the correct command isgetstandingsgetseasonstandings(requiresseasonid).— not available. Usegetlivescoresgetdailyschedule()for today's matches./getteamsquad—getteamrostergetteamprofiledoes NOT return players. Usegetseasonleadersfor PL player IDs, thengetplayerprofile.— the correct command isgettransfersgetseasontransfers(requiresseasonid+tmplayerids)./getmatchresults— usegetmatchgeteventsummarywith aneventid.— usegetplayerstatsgeteventplayersstatisticsfor match-level stats, orgetplayer_profilefor career data./getscores— usegetresultsgeteventsummarywith anevent_id.— usegetfixturesgetdailyschedulefor today's matches orgetseason_schedulefor a full season.— usegetleaguetablegetseasonstandingswith aseason_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:
- Catch it silently — treat the failure as an exploratory miss.
- Try alternatives — if an eventid returns no data, call
getdailyschedule() orgetteam_schedule() to discover the correct ID. - 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: getseasonleaders or getmissingplayers 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 geteventplayers_statistics for player data
Error: getteamprofile returns no players Cause: This command does not return squad rosters — this is expected behavior Solution: For PL teams, use getseasonleaders to find player FPL IDs, then getplayerprofile(fpl_id="...")
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 geteventxg 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 searchteam(query="team name") to find team IDs, or getdailyschedule / getseason_schedule to find event IDs. Never guess IDs.