Files
nike/docs/sportsdb_api_v1_reference.md
Robert Helewka ee8436d5b8 feat: implement MCP server and dashboard for football data platform
Add complete Nike football data platform with:
- FastMCP server exposing football data tools over HTTP
- RapidAPI client for free-api-live-football-data integration
- Bootstrap web dashboard with live match/standings views
- REST API endpoints for dashboard consumption
- Docker support with multi-stage build
- Comprehensive README with architecture docs
- Minimal .gitignore replacing verbose Python template
2026-03-21 18:19:42 +00:00

16 KiB
Raw Permalink Blame History

TheSportsDB API V1 — Validated Reference

Validated on: 2026-03-09 using free test key 3 Base URL: https://www.thesportsdb.com/api/v1/json/{key} Auth: Key embedded in URL path (free test key: 3, premium: same Patreon key as V2) Source samples: docs/api_samples/sportsdb/

This document covers ONLY what was tested and confirmed with real API calls. V2 endpoints are NOT covered — they require a premium key and have not been tested.


Free Key Limitations

The free key (3) returns limited results (typically 5 rows for standings, returns wrong/random events for some lookups). The response structure and field names are valid regardless — only row count and relevance are affected.


Validated IDs

Entity TheSportsDB ID Notes
MLS 4346 Listed as "American Major League Soccer"
English Premier League 4328 "English Premier League"
Toronto FC 134148 Short: TOR, league: 4346
Arsenal 133604 Short: ARS, league: 4328
Federico Bernardeschi 34148472 Position: Winger

Endpoints

1. Search Teams

GET /searchteams.php?t={team_name}

Tested with: Toronto FC, Arsenal Response wrapper: {"teams": [...]} Returns: Array of matching team objects (1 exact match each)

Validated fields (all values are strings or null):

Field Example (TFC) Example (Arsenal) Notes
idTeam "134148" "133604" String, not int
idESPN null "359" Nullable
idAPIfootball "1601" "42" Cross-ref to API-Football
intLoved "2" "9" String of int
strTeam "Toronto FC" "Arsenal" Full name
strTeamAlternate "" "Arsenal Football Club, AFC, Arsenal FC" Comma-separated aliases
strTeamShort "TOR" "ARS" 3-letter code
intFormedYear "2006" "1892" String of int
strSport "Soccer" "Soccer"
strLeague "American Major League Soccer" "English Premier League" Primary league
idLeague "4346" "4328" String of int
strLeague2..strLeague7 "Leagues Cup" "FA Cup", "EFL Cup", etc. Additional leagues/cups
idLeague2..idLeague7 "5281" "4482", "4570", etc. IDs for above
strDivision null null
idVenue "16782" "15528" String of int
strStadium "BMO Field" "Emirates Stadium"
strKeywords "" "Gunners, Gooners" Nicknames
strRSS "" ""
strLocation "Toronto, Ontario" "Holloway, London, England"
intStadiumCapacity "30991" "60338" String of int
strWebsite "www.torontofc.ca" "www.arsenal.com" No protocol
strFacebook "www.facebook.com/torontofc" "www.facebook.com/Arsenal"
strTwitter "twitter.com/torontofc" "twitter.com/arsenal"
strInstagram "www.instagram.com/torontofc" "instagram.com/arsenal"
strDescriptionEN Full paragraph Full paragraph English bio, \r\n separated
strDescriptionDE..strDescriptionPL null or text null or text 14 language variants
strColour1 "#B81137" "#EF0107" Hex color
strColour2 "#455560" "#023474"
strColour3 "" "#9C824A" May be empty
strGender "Male" "Male"
strCountry "Canada" "England"
strBadge URL URL PNG badge
strLogo URL URL PNG logo
strFanart1..strFanart4 URL or null URL or null Fan art images
strBanner URL URL Banner image
strEquipment URL URL Kit/equipment image
strYoutube "www.youtube.com/torontofc" "www.youtube.com/ArsenalTour"
strLocked "unlocked" "unlocked"

Key observations:

  • ALL numeric values are returned as strings (e.g., "134148" not 134148)
  • Team can belong to up to 7 leagues/cups
  • idAPIfootball provides cross-reference to API-Football IDs

2. Lookup Team by ID

GET /lookupteam.php?id={idTeam}

Tested with: 134148 (Toronto FC) — but free key returned Arsenal instead (known free-key bug) Response wrapper: {"teams": [...]} Fields: Identical to search response above.

⚠ Free key pitfall: Lookup by ID returned the wrong team (Arsenal instead of TFC). This is a free-key limitation, not a field-structure issue. The response shape is the same as search.


3. Previous Team Events (Last Results)

GET /eventslast.php?id={idTeam}

Tested with: 134148 (Toronto FC) Response wrapper: {"results": [...]} ← Note: key is results, NOT events Returns: Last 5 results for the team (free key returned 1)

Validated event fields:

Field Example Notes
idEvent "2425216" String
idAPIfootball "1514800" Cross-ref, nullable
strEvent "Toronto FC vs Polissya Zhytomyr" Match title
strEventAlternate "Polissya Zhytomyr @ Toronto FC" Away @ Home format
strFilename "Club Friendlies 2026-02-14 Toronto FC vs Polissya Zhytomyr" League + date + teams
strSport "Soccer"
idLeague "4569" String — league of this match
strLeague "Club Friendlies"
strLeagueBadge URL League badge
strSeason "2026" or "2025-2026" for Euro leagues
strDescriptionEN "" Match description (usually empty)
strHomeTeam "Toronto FC"
strAwayTeam "Polissya Zhytomyr"
intHomeScore "2" String of int, null if not played
intAwayScore "1" String of int, null if not played
intRound "0" String, "0" for friendlies
intSpectators null Nullable
strOfficial "" Referee, often empty
strTimestamp "2026-02-14T09:45:00" ISO-ish, no timezone
dateEvent "2026-02-14" YYYY-MM-DD
dateEventLocal "2026-02-14" Local date
strTime "09:45:00" UTC time
strTimeLocal "04:45:00" Local time
strGroup "" Group stage name, nullable
idHomeTeam "134148" String
strHomeTeamBadge URL Team badge
idAwayTeam "140180" String
strAwayTeamBadge URL Team badge
intScore null Aggregate/extra score? Always null in sample
intScoreVotes null Always null
strResult "" Text result summary, empty
idVenue "16782" String
strVenue "BMO Field"
strCountry "Canada"
strCity "" Often empty
strPoster "" Image URLs, often empty
strSquare ""
strFanart null
strThumb ""
strBanner ""
strMap null
strTweet1 ""
strVideo "" Highlight video URL
strStatus "Match Finished" See status values below
strPostponed "no" "no" or "yes"
strLocked "unlocked"

Known strStatus values: "Match Finished", "Not Started", null (old events)


4. Next Team Events (Upcoming)

GET /eventsnext.php?id={idTeam}

Tested with: 134148 (Toronto FC) Response wrapper: {"events": [...]} ← Note: key is events (different from eventslast) Returns: Next 515 upcoming events

Fields: Identical to the event structure in section 3 above, except:

  • intHomeScore / intAwayScore = null (not yet played)
  • strStatus = "Not Started"
  • dateEventLocal / strTimeLocal may be null
  • strThumb may have a URL (match preview image)

⚠ Free key pitfall: Free key returned Bolton Wanderers fixtures instead of Toronto FC fixtures. The event field structure is still valid.


5. Event Lookup by ID

GET /lookupevent.php?id={idEvent}

Tested with: 2425216 Response wrapper: {"events": [...]} Returns: Single event in array

Fields: Same event structure as section 3. All fields present.

⚠ Free key pitfall: Free key returned a completely different event (Liverpool vs Swansea 2014 instead of TFC vs Polissya 2026). The field structure is still valid — the values are just wrong.


6. Search Players

GET /searchplayers.php?p={player_name}

Tested with: Bernardeschi Response wrapper: {"player": [...]} ← Note: key is player, singular Returns: Array of matching players

Validated fields:

Field Example Notes
idPlayer "34148472" String
idTeam "134781" String
strPlayer "Federico Bernardeschi" Full name
strTeam "Bologna" Current team (may not match Toronto FC if transferred)
strSport "Soccer"
strThumb URL Player thumbnail
strCutout URL Player cutout image
strNationality "Italy"
dateBorn "1994-02-16" YYYY-MM-DD
strStatus "Active" "Active" or "Retired"
strGender "Male"
strPosition "Winger"
relevance "28.93..." Float as string, search relevance score

Note: V1 player search returns a minimal field set (13 fields). V2 lookup/player/{id} is expected to return full bio, height, weight, description, etc. — not yet tested.


7. Standings / League Table

GET /lookuptable.php?l={idLeague}&s={season}

Tested with: l=4328&s=2025-2026 (EPL), l=4346&s=2026 (MLS) Response wrapper: {"table": [...]} Returns: Standings rows (free key: 5 rows; premium: full table)

Validated fields:

Field Example (EPL) Example (MLS) Notes
idStanding "8793837" "8790287" String
intRank "1" "1" String of int
idTeam "133604" "150261" String
strTeam "Arsenal" "San Diego FC"
strBadge URL /tiny URL /tiny Tiny badge variant
idLeague "4328" "4346" String
strLeague "English Premier League" "American Major League Soccer"
strSeason "2025-2026" "2026" Euro vs US format
strForm "WWWDD" "WWW" Recent results W/D/L
strDescription "Promotion - Champions League..." "Promotion - MLS (Play Offs...)" Qualification zone
intPlayed "30" "3" String of int
intWin "20" "3"
intLoss "3" "0"
intDraw "7" "0"
intGoalsFor "59" "8"
intGoalsAgainst "22" "0"
intGoalDifference "37" "8"
intPoints "67" "9"
dateUpdated "2026-03-09 06:01:21" "2026-03-09 06:00:50" Last update timestamp

Season format:

  • European leagues: "2025-2026"
  • MLS / calendar-year leagues: "2026"

MLS standings note: MLS has two conferences (East & West). The API returns both mixed together — ranks restart at 1 for each conference. There is no explicit conference field. You must use the rank patterns or strDescription to infer conference grouping.


8. Events by Date

GET /eventsday.php?d={YYYY-MM-DD}&s=Soccer       # all soccer events
GET /eventsday.php?d={YYYY-MM-DD}&l={idLeague}    # filtered by league

Tested with: d=2026-03-09&s=Soccer, d=2026-03-09&l=4346 Response wrapper: {"events": [...]} or {"events": null}

Fields: Same event structure as section 3.

Results:

  • Global soccer on 2026-03-09: Returned 1 event (Melbourne Victory vs Western Sydney, from 2014) — free key returns stale/random data
  • MLS on 2026-03-09: Returned {"events": null} — no matches or free key limitation

⚠ Free key: Events-by-date is unreliable on free key. The field structure matches section 3 but the actual events returned are wrong/old. Premium key should return correct current-day events.


Response Patterns Summary

Endpoint Wrapper Key Null when empty
searchteams.php teams Unknown (always had results)
lookupteam.php teams Unknown
eventslast.php results Unknown
eventsnext.php events Unknown
lookupevent.php events Unknown
searchplayers.php player Unknown
lookuptable.php table Unknown
eventsday.php events Yes — returns {"events": null}

Data Type Warning

All numeric fields are returned as strings. Every id*, int*, and numeric value must be cast to int before use:

team_id = int(team["idTeam"])      # "134148" → 134148
capacity = int(team["intStadiumCapacity"])  # "30991" → 30991

V1 Endpoints NOT Tested

These V1 endpoints exist in the API but were not called during discovery:

Endpoint Purpose
searchevents.php Search events by name
eventsnextleague.php Next events for a league
eventspastleague.php Previous events for a league
eventsseason.php All events in a season
lookup_all_players.php Full squad (V1 version)
lookupplayer.php Detailed player profile
all_sports.php All sports (used only for health check)
all_leagues.php All leagues
search_all_leagues.php Search leagues

V2 Endpoints — NOT TESTED (Premium Key Required)

These are the V2 endpoints built into nike/sportsdb.py but entirely untested. All field names in the code are guessed from TheSportsDB OpenAPI docs and have NOT been validated against real responses.

Endpoint Guessed wrapper key What we don't know
GET /search/league/{name} search Actual field names
GET /search/team/{name} search May differ from V1
GET /search/player/{name} search May differ from V1
GET /lookup/event/{id} lookup or events May have extra fields vs V1
GET /lookup/event_stats/{id} lookup Field names like strStat, intHome, intAway are guesses
GET /lookup/event_timeline/{id} lookup Field names like intTime, strTimeline, strPlayer are guesses
GET /lookup/event_lineup/{id} lookup Fields like intSquadNumber, strSubstitute, strHome are guesses
GET /lookup/player/{id} lookup or players May have strHeight, strWeight, strDescriptionEN
GET /lookup/player_contracts/{id} Unknown Unknown
GET /lookup/player_honours/{id} Unknown Unknown
GET /lookup/event_tv/{id} Unknown Unknown
GET /lookup/venue/{id} Unknown Unknown
GET /list/teams/{leagueId} list Unknown
GET /list/seasons/{leagueId} list Unknown
GET /list/players/{teamId} list Field names for squad members unknown
GET /schedule/next/league/{id} Unknown Unknown
GET /schedule/previous/league/{id} Unknown Unknown
GET /schedule/next/team/{id} Unknown Unknown
GET /schedule/previous/team/{id} Unknown May match V1 eventsnext or differ
GET /schedule/full/team/{id} Unknown Unknown
GET /schedule/league/{id}/{season} Unknown Unknown
GET /livescore/soccer livescore All field names guessed

Next Steps (After Getting Premium Key)

  1. Set SPORTSDB_KEY=your_key in .env
  2. Run python scripts/discover_sportsdb.py — it auto-detects premium and tests all V2 endpoints
  3. Review saved JSON files in docs/api_samples/sportsdb/
  4. Update this document with V2 validated fields
  5. Fix sportsdb.py client if any URL paths or wrapper keys are wrong
  6. Design schema.sql from real field names (current schema is drafted from guesses)
  7. Fix server.py tool formatters to use actual field names