What's new
Recent improvements to the Big Balls Sports Data API, in reverse chronological order.
2026-08-31
-
The
/v1/stored/*endpoints now take the canonical league code, and an unknown league says so instead of returning nothing.GET /v1/stored/players,/v1/stored/matchesand/v1/stored/standingsmatched the league's DISPLAY NAME and nothing else, so?league=La Ligareturned rows while?league=laligareturned an empty array with a200and no explanation — indistinguishable from a league we genuinely hold no rows for. Measured on all three endpoints on 2026-08-31. They now resolve through the same shared resolver/v1/standings,/v1/teamsand/v1/leagues/{id}/*already use, which accepts the canonical key, the brand alias AND the exact display name, so nothing that worked before has stopped working. An unrecognised league is now a400naming the value you sent, with the list of valid codes, rather than a silent empty200. The 2026-08-27 entry below said/v1/teams?league=takes the canonical key "like every other league-filtered endpoint" — these three were the endpoints that disagreed with that sentence, and now they don't. League codes are listed at/docs/league-codes. -
?season=now means the season, not the calendar year. Row counts will change on most sports, and the old numbers were wrong. A season query filtered on the calendar year of kickoff, which is correct only for leagues whose season starts and ends inside one year. For everything else it returned roughly half of one campaign and half of the next. Measured on 2026-08-31 for?season=2025: NBA returned 1,310 matches of which 767 (59%) belonged to another season, EPL returned 378 of which 192 (51%) did, and Serie A, La Liga, Bundesliga and Ligue 1 all sat between 54% and 56% wrong. NHL failed differently and worse: it returned no wrong rows at all and 55% of the season missing — 626 of 1,391 — because a season starting in October only overlaps a calendar year for three months, so the response was complete-looking, internally consistent and half a season. There was no way for a caller to tell.?season=2025now means the campaign that STARTS in 2025: the 2025-26 season for the EPL, NBA and NHL, and the 2025 season for MLB, MLS and WNBA, whose seasons genuinely are calendar years and whose results are unchanged. Each league's own start month is resolved from the same registry the season labels come from, so a league that plays May to August is not forced into a rollover it does not have. This affectsGET /v1/players/{id}/stats,GET /v1/teams/{id}/matches,GET /v1/teams/{id}/statsandGET /v1/matcheswherever?season=is sent. If you were compensating for the old behaviour — requesting two years and merging, or discarding rows outside a date range — you can stop, and you should, because that compensation now double-counts. The parameter is documented per endpoint in the OpenAPI spec; one route previously described the old behaviour in its own docs as "returns only the 2025 half. Documented as-is", and documenting it did not make it usable.
2026-08-28
-
NBA and NHL injury rows have stopped updating, and
updated_atis how you tell. The upstream provider feeding these two stopped returning data.GET /v1/injuries?sport=basketballand?sport=ice_hockeystill return 200 with rows, but the newest NBA row carries anupdated_atof 2026-07-26 and the newest NHL row 2026-07-01, so those responses describe a moment months back rather than today. Readupdated_aton each row before treating a status as current. Soccer injuries are unaffected and still current. The 2026-06-02 entry below announced these two as live and refreshed every 30 minutes, and that is no longer true. -
NFL injuries, depth charts and team stats are now served. Three endpoints, 64,425 rows, every one of which used to
404. The tables were being filled daily and nothing read them.GET /v1/nfl/injuriesreturns the league's mandated weekly injury and practice report (34,810 rows, seasons 2020 through 2025); call it bare and it resolves to the newest week, which it names inmeta.resolved.GET /v1/nfl/depth-chartsreturns each team's open chart intervals, or the chart as it stood at a past instant with?as_of=; rows carryopen_intervalrather thancurrent, andmeta.season_coveredtells you which season the chart belongs to, because labelling a chart "current" is a claim about a season that has not started.GET /v1/nfl/team-statsreturns team-week offensive totals including EPA and CPOE. All three carrymeta.data_as_offrom the row's ownupdated_at, and the writer only rewrites a row that actually changed, so that timestamp ages honestly when a feed goes quiet. Coverage reports these asoff_seasonuntil Week 1 on 2026-09-09, notlive. See/nfl-apiand/injuries-api. -
GET /v1/injuries?sport=american_footballreturns data instead of a404that contradicted itself. It answered "we do not serve injuries for 'american_football' yet" while thesuggested_fixin the same payload listed american_football as live, with 34,810 rows sitting in the table. It now serves the NFL rows through the unified endpoint. Two nflverse team abbreviations disagreed with ours (LAandWAS), which servedteam: nullfor those two franchises; both are mapped.baseballhas been removed from the supported list on the same endpoint, because no baseball injury table exists and it was passing the gate only to404with asuggested_fixthat excluded it. -
Soccer injuries are current again across six leagues, MLS included, and
?league=narrows to one competition. Two defects were stacking. The season the ingest asked for rolled over in September for every league, which is right for European clubs and wrong for MLS and Brasileirão, whose seasons are calendar years: on 2026-08-27 that fetched MLS season 2025, returning a complete, plausible, nine-month-stale set that looks exactly like success. Andas_ofwas computed fromfetched_at, which the ingest re-stamps on every row every 30 minutes, so the endpoint reported today's timestamp over fixtures that had ended on 2026-05-29, 91 days earlier.as_ofnow anchors tofixture_date, so it can report stale when the data is stale. Measured today, all six leagues hold absence records against fixtures through 2026-08-30: EPL 3,264, La Liga 3,010, Serie A 2,921, Ligue 1 2,743, Bundesliga 2,473, MLS 2,381.?league=previously resolved and was then ignored by the query, so every league returned the same rows; each league now gets its own rows and its ownas_of. Supported values areEPL,La Liga,Serie A,Bundesliga,Ligue 1,MLSandUEFA Champions League; anything else is a400that lists them rather than a silent widening to all of them. -
New: a page per league for soccer injuries.
/premier-league-injury-api,/la-liga-injury-api,/serie-a-injury-api,/bundesliga-injury-apiand/ligue-1-injury-api. Each one reads its own league's record count and newest fixture date at render time and prints them, so a page states how current that league actually is instead of asserting a refresh rate. They count absence records, not injuries: the reason field holds 134 distinct strings in production and no pattern splits injury from suspension without leaking in both directions, so the pages say the thing that is true of every row. -
Combat sports no longer serve summed judges' scorecards as a final score. The cards are now published as the result they always were.
matches.linescoreholds judges' totals for MMA and boxing, not period points, and the score was being summed anyway: a UFC bout scored 30-27 by all three judges was served asscore: {"home": 90, "away": 81}. Nobody wins a fight 90-81. On 43 finished bouts in production,scoreis nownullwithscore_status: "unavailable", and two new fields carry the real answer:judge_scoresrepublishes the cards, anddecisiongiveswinner,method(unanimous_decision,split_decision,majority_decisionordraw),rounds_scoredandjudges. A bout that has scorecards went the distance by definition, so this is a result we already held rather than an inference.decisionis omitted when the cards are not readable, because suppressing a wrong score must not depend on being able to derive a right one. Cancelled bouts also stop serving a placeholder0-0, which for a fight is indistinguishable from a real draw. The 2026-07-25 entry below said MMA serves fight cards, matchups and odds but not results; for these 43 decisions that is no longer true, and the fighter records and non-decision finishes it also named are still not available. See/mma-api,/ufc-apiand/boxing-api. -
New:
/about, who builds this and why it is published the way it is. The signup, key and call counts on it are read live rather than typed, on the page whose argument is that we publish what we can measure. It is the human counterpart to/coverageand/attribution.
2026-08-27
-
GET /v1/teams?league=takes the canonical league key, like every other league-filtered endpoint. It matched the league's display name and nothing else, so?league=La Ligareturned 23 teams and?league=laligareturned an empty array with a200and no explanation. It now resolves through the same shared resolver/v1/standingsand/v1/leagues/{id}/*use, which accepts the key, the brand alias and the exact display name, so nothing that worked has stopped working. An unknown league now returns a400naming the value you sent instead of a silent empty200. The 2026-07-16 entry below said sport and league naming resolves through one canonical registry everywhere; this endpoint was the exception, and now it isn't. League codes are listed at/docs/league-codes. -
GET /v1/standingsno longer returns clubs that are not in the league. La Liga served 23 rows for a 20-team league, with three second-division sides sitting atgames_played: 0and holding ranks that were already taken: rank 6 was both Celta Vigo, with 38 games played, and Deportivo, with none. Anyone renderingrankgot a Segunda side in La Liga's sixth place. Every loaded European league had the same shape. A row is now dropped only when its rank is non-null, it has no games played, and another team holds that same rank in the same league and season with games played, so a genuine mid-season tie survives and a table at season start survives whole. Verified live today: La Liga, EPL and Serie A return 20 rows, Bundesliga and Ligue 1 return 18, ranks contiguous from 1 with no duplicates. Leagues that store every row at ranknull, such as the NHL and NCAAF, are untouched. -
La Liga, Serie A, Bundesliga, Ligue 1 and college football are now polled for live scores. Four of our six soccer competitions and both college football divisions were absent from the score worker's league list, so they were never polled: across 60 days of finished matches, the worker had settled zero La Liga, Serie A and Ligue 1 games, and zero NCAAF games ever. Two things follow from a league being polled. Matches in those leagues now report
status: "live"while they are being played, wherescheduledpreviously covered both "not started" and "in progress" and a healthy in-play match was indistinguishable from a stuck one. And they settle promptly rather than late. College football needed more than a list entry: ESPN serves FBS and FCS from one endpoint, separated by agroupsparameter, and its default response is capped at 25 events regardless oflimit, so both divisions are now requested explicitly. See/ncaaf-apiand/soccer-api. -
The four soccer league pages now render real per-league data: xG boards, top-scorer boards and the upcoming fixture list.
/la-liga-api,/serie-a-api,/bundesliga-apiand/ligue-1-apieach publish that league's own standings table, seasons held, per-data-type coverage, endpoint examples with that league's real ids, and now a ten-row expected-goals board withnpxg,xaand goals-above-xG, a ten-row scorer board, and the next fixtures we hold. Every figure is read from the API at render time. One thing to know if you are calling these yourself:/v1/leagues/{id}/xg-leadersand/v1/leagues/{id}/top-scorerskey on the hyphenated league code where/v1/standingsand/v1/matchestake the concatenated one.serie-aandligue-1return200on the boards;serieaandligue1return404.laligaandbundesligaare the same either way. Send?season=explicitly on/v1/leagues/{id}/top-scorers: it defaults to 2025 and will keep serving 2025 after the rest of your data has moved on. -
Cricket coverage now names 13 competitions instead of 2, and
GET /v1/cricket/matches/{id}/scorecardappears in a coverage surface for the first time. Cricket is our deepest sport by competition count, 768 competitions and 23,484 matches from 2001 to 2027, and the coverage manifest named two leagues of it. The scorecard endpoint serves full ball-by-ball batting and bowling cards for 22,539 finished matches, 97.8% of the sport, and was documented nowhere at all. Both are now in every generated surface, including the ones assistants read. See/cricket-api. -
/docs/oddsnow lists the sportsbooks we actually carry: 25 of them, 6 reaching us from two independent providers. Pinnacle, Matchbook, ProphetX, Novig, Kalshi, Polymarket and Bet365 are among them. The list is generated from live odds joined to the book reference table, so a book we hold a reference row for but no longer receive is not published as carriage, and a build check fails when the committed copy drifts. The six two-source books are the set thetwo_source_agreedverification described in the 2026-07-13 entry below can actually corroborate.
2026-08-11
- NCAA FCS (Football Championship Subdivision) is now live — a separate league from NCAAF, sharing the same American Football sport.
GET /v1/matches?sport=american_football&league=ncaaf-fcscovers 139 FCS teams and a decade of regular-season history (2016-2025, weeks 1-15 — the FCS postseason playoff is not loaded yet).GET /v1/players/:id/statsnow returns real per-game box-score stats for FCS players too — strong for 2021-2025 (88-99% of games), sparse before that: the upstream source's own historical FCS box-score collection doesn't reach back past 2021, a source limit, not a gap on our end. End-to-end across the 7,113 games that touch an FCS team: about 58%, 1,087,323player_statsrows across 42,761 players. No standings, Elo ratings, or odds for FCS yet. - FBS-vs-FCS "buy games" are no longer missing from NCAAF (FBS) — 1,024 of them now carried. These were previously excluded entirely (2026-08-08 entry below) because the FCS opponent had no resolvable roster.
GET /v1/matches?sport=american_football&league=ncaafnow returns 9,204 games (up from 8,180), and shows up on either team's own record viaGET /v1/teams/:id/formandGET /v1/teams/:id/h2h-intelligence. Per-game player-stat coverage for FBS rose from ~87% to 93% of finished games as a result (4,092,224 rows across 8,188 games).
2026-08-08
- NCAAF history is now a full decade, not one season.
GET /v1/matches?sport=american_football&league=ncaafnow returns 8,180 games spanning 2016 through the 2026 schedule, up from 2025-only.GET /v1/players/:id/statsnow returns real per-game box-score stats for NCAAF, live for 2016-2025 and covering about 87% of FBS games played in that span — 3.5Mplayer_statsrows across 7,166 games. The uncovered ~13% is a structural gap, not partial rollout: FBS-vs-FCS "buy games" aren't carried at all today, since FCS opponents are out of scope.
2026-08-04
- NFL Elo ratings are live and queryable.
GET /v1/teams/:id/elonow returns real ratings for all 32 NFL teams, backed by a full historical recompute (3,386elo_historyrows across the 2020-2025 seasons). This was blocked until today by a same-city team-linking bug: the 2025 season had every New York Giants game attributed to the Jets and every Los Angeles Chargers game attributed to the Rams, so an Elo computation would have rated the wrong participants. The bug is fixed (both in the ingest code and in the 34 already-wrong match rows), so ratings now reflect who actually played. - The dashboard shows your API key and first call before anything else. The key reveal and the ready-to-run first-call card now render as the first thing you see after signup, ahead of onboarding prompts and account setup. If you've felt like the key was buried under other UI on your first visit, it isn't anymore.
- Signup now captures which sports you're building for, from more than one signal. The platform reads both an explicit intent you declare and attribution from how you arrived (landing page, referrer), and supports declaring interest in more than one sport. This powers more relevant onboarding and, going forward, more relevant product updates for the sports you actually use.
- New: an agent-oriented quickstart on the dashboard. Alongside the existing curl-based first call, the dashboard now offers a copy-paste snippet built around the
@bigballsdata/sdknpm package, aimed at developers wiring the API into an AI agent or IDE assistant rather than a terminal. Proof-of-concept, feedback welcome. - Homepage and landing pages no longer reference the 2026 World Cup as current. With the tournament over, the marketing pages that featured live/upcoming World Cup framing have been updated to reflect that it's concluded.
2026-08-03
- Gateway errors now return a real
503for transient database issues, not a raw500. A momentary connection-pool exhaustion or statement timeout previously surfaced as an unhelpful generic500. It now returns503with retry guidance, which is the honest signal for a transient condition worth retrying. GET /v1/matches?status=accepts the status vocabulary from our other endpoints, not just its own.upcoming,not_started,in_progress,ongoing,final, andftare now aliased to this endpoint's canonicalscheduled/live/finishedvalues, so a term that already works on/v1/wc2026/matchesorlive-statsno longer 400s here. The error message for a genuinely invalid value now names the exact value you sent.- Marketing pages now pull their sport and league counts from
GET /v1/coverage, live, instead of a hardcoded number. Pages that advertise "N sports" no longer drift out of sync with what the API actually serves. - New:
/tools/odds-calculator, a free, no-signup odds converter and payout calculator. Convert American, decimal, and fractional odds, see implied probability, and calculate payout on a stake, entirely client-side. A documented reference implementation of the exact conversion math, not a live odds feed. - Background workers no longer silently stall on a Redis hang. The hourly job that closes out stale
livematches, and the live-stats and score-update workers, are now resilient to a dead or slow Redis connection. Previously a Redis hang could leave finished games showing aslivefar longer than expected; both are now isolated so one failing dependency doesn't block the others.
2026-07-27
- New
route_not_founderror code, plus sport-prefixed route aliases. A request to a path that doesn't exist now gets a distinctroute_not_found(404) instead of being confused with a valid-route-but-missing-resource error. Common URL-guessing mistakes for leagues, matches, and countries now resolve via alias instead of 404ing. GET /v1/sportsandGET /v1/coverageno longer require an API key. Both are now public (IP rate-limited), so you can explore what the API covers before you ever sign up.- Errors for a mismatched player ID now name the real cause. Passing a
bdl_player_idoraf_player_idfrom our own/v1/matcheslineups or/v1/teams/{id}/squadresponses into/v1/players/{id}now says so explicitly, and points you at/v1/players?name={name}to get the right Big Balls id first. The same clarity now extends to invalid, non-UUID ids passed to team, NBA lineup, and prediction endpoints, with asuggested_fixpointing at the right discovery endpoint. /v1/matches/:id/oddsand/v1/matches/:id/eventsnow fall back to stored data instead of returning nothing. Previously these returnednullif live odds or events weren't available, even when the data existed in storage. The odds endpoint now falls back to aggregated, vendor-free stored odds; the events endpoint serves stored match events for non-soccer sports before attempting a live fetch.- New: activation recovery emails. Signups with no API call in their first 24 hours, or a high error rate across their first several calls, now get an email with a working example tailored to their declared sport and common failure patterns.
2026-07-26
- Sport entitlements are now enforced, not just modeled. Roughly 119 routes now gate access by your plan: request a sport your plan doesn't include and you get a
403naming the fix. NBA play-by-play (GET /v1/nba/games/:gameId/plays) and shot coordinates (GET /v1/players/:id/shots) are gated as one Edge-tier unit, and/intelligence/value-finderplus WebSocket connections narrow to your entitled sports rather than blanket-403ing. - MLB scores now agree between list and detail views. Scheduled games no longer show a fabricated
0-0in one view andnullin the other; both now correctly shownullbefore kickoff and the real score once the game starts. - Finished and live matches derive their score from
linescore, not stalematch_stats. A match with a completelinescoreno longer risks showing0-0because a separate stats field hadn't caught up./v1/matchesand/v1/matches/:idnow sum the per-periodlinescorewhen it's available. /v1/odds/divergencesno longer republishes verbatim vendor odds. The raw per-provider price fields are gone from the response, in line with vendor redistribution terms. The computed disagreement signals (category,secondary_categories,magnitude) are unaffected, still our own data.- Catalogue endpoints no longer claim
confidence: 1for an unknown source./v1/sportsand/v1/leaguesnow correctly reportsource: 'internal-catalog'for our own data; a genuinely unknown source now reportsconfidence: 0instead of a misleadingly confident1. - NCAAF coverage claims now match what's actually there, and every sport must declare its data types explicitly. The
/ncaaf-apipage states real figures (91% of finished games carry multi-book odds, no college football play-by-play, box scores for roughly 13% of games), and the coverage manifest now requires every sport to declare each data type rather than allowing silent gaps. A newratingsdata type reports live for NBA and NHL, partial for soccer. - Cricket team lookups now search across every league in the sport, not just one. Previously a team lookup was scoped to a single league, which could mint a duplicate team when two data sources filed the same club under different league ids.
GET /v1/matches/:idno longer hangs for up to 30 seconds when live enrichment is slow. Each field fetch is now capped at 2 seconds; on a timeout or missing live coverage, the endpoint serves the stored row with an honest coverage note instead of leaving you waiting./v1/matches/:id/oddsand/v1/matches/:id/eventsno longer require?sport=. Both now infer the sport from the match id itself.- NFL team identity bug fixed at the source. The New York Jets/Giants and Los Angeles Rams/Chargers were being resolved to the same team by a same-city name match with no franchise qualifier. New 2025-season data now links to the correct team; see 2026-08-04 above for the historical data repair.
- Boxing fighter bios now available:
GET /v1/athletes?sport=boxing. Fight cards and bout results are still coming soon and are marked as such; profile data (name, nationality, physicals, weight class) is live today.
2026-07-25
- MLB games no longer show a fabricated
0-0before kickoff. A scheduled game's linescore now correctly returnsnullinstead of a placeholder{"home":[0],"away":[0]}that read as a real, in-progress score. - Email sign-in accepts the full code length Supabase issues, not just 6 digits. The sign-in form previously hardcoded a 6-digit expectation and truncated or rejected longer codes before they reached Supabase. It now accepts the full 6-10 digit range and passes the code through unmodified, fixing sign-in for anyone who received a longer code.
- MMA coverage now states what it actually serves: fight cards, bout matchups, and odds, not results. The
/mma-apiand/ufc-apipages previously implied bout results, fighter profiles, and win-loss records were available. They aren't yet; both pages and the coverage manifest now mark results as coming soon rather than claiming them. - Formula 1 coverage now states what it actually serves: the 2026 calendar, nothing else yet. No scores, stats, or historical data exist for F1 today; the
/formula-1-apipage and coverage manifest no longer imply otherwise. - New: a Nevada State Athletic Commission boxing results scraper (dry-run only). Parses NSAC boxing PDFs for bout results, method, round, and judges' scorecards, successfully extracting 13 events and 108 bouts from 2025 test data. No database writes yet; results are scoped to US state athletic commission public records (Nevada first), not bouts sanctioned outside the US.
2026-07-24
-
TypeScript SDK: install
@bigballsdata/sdk(0.1.1). The SDK now ships under its correct name —npm install @bigballsdata/sdk(orpnpm install @bigballsdata/sdk). This release fixes the two bugs that made the earlier@bigballsports/[email protected]unusable: the client now defaults to the correct base URLhttps://api.bigballsdata.com(0.1.0 defaulted to a domain we don't own, which parked instead of answering), and the published package now resolves on install — itsmain/types/exportspoint at the shippeddist/, soimport { BigBallSportsClient } from '@bigballsdata/sdk';works from a clean project. One import, every sport. If you installed@bigballsports/sdk, switch to@bigballsdata/sdk— the old package is not maintained. -
Sign in from a different device than you requested the link on. Email sign-in now also gives you a 6-digit code. Request the link on your laptop and you can finish signing in on your phone (or the other way round) by typing the code, instead of being forced to open the link on the exact device that requested it. The emailed link still works as before — the code is just an additional path for when the link lands on the wrong device.
-
New: the
/attributionpage — the data sources behind the API, credited. A public, footer-linked page now lists the licensed open datasets we build on and the exact credit each one's license requires. It's where source and license notices live as we bring more open data online. -
The
/hockey-apiand/nhl-apipages now show real, working examples. Both pages were rebuilt so every endpoint and code sample is copy-paste-runnable against live data — 12 seasons of NHL coverage, xG leaders, and samples that actually return what they show. If a sample on those pages didn't work for you before, it does now.
2026-07-19
-
New:
GET /v1/leagues/{id}/xg-leaders, expected-goals leaderboards for the big-five European leagues. One call returns a season xG leaderboard forepl,laliga,serie-a,bundesliga, orligue-1— the same league codes/v1/leagues/{id}/top-scorersalready takes. Each row carriesxg,xa,npxg,xg_chain,xg_buildup,goals,assists,shots,key_passes,matches,minutes, and a derivedgoals_above_xg(goals minus xG) so you can see finishing over- and under-performance directly. Sort with?stat=xg|xa|npxg|goals|assists|shots|key_passes(defaultxg), and narrow with?season=,?min_minutes=,?limit=. History runs back to season 2014, well before our match coverage starts, so this is usable for long-range model training. Refreshed daily.Two things to know before you build on it. Rows are keyed by
player_name, not by a BigBallsplayer_id— the entity bridge between our xG source and our players table is not built yet, so we do not offer per-player or per-match xG endpoints and you should not join these rows to/v1/players/{id}on identity. And any season row we cannot attribute to a league is excluded from the board rather than bucketed somewhere convenient, so a leaderboard only ever contains players from the league you asked for.
2026-07-16
-
New:
GET /v1/coverage, one call that tells you exactly what we have. A single request now returns a full, DB-derived coverage snapshot: every sport and league with data, the canonical name plus aliases, a copy-pasteexample_call, the seasons held, freshness (data_through,next_event,last_ingested), and a per-datatype breakdown (scores,player_stats,player_season_stats,odds,player_props,team_stats, and more) with a coverage percentage and a derived status (current,in_season,historical,thin,none) for each. It is generated from the actual rows, not a hand-maintained list, so it cannot drift from reality: a datatype we do not have reports an honest0%rather than a false badge. Narrow to one sport with?sport=, which accepts the canonical name or any alias. This is the fastest way to see what is worth integrating before you write a line of code. -
NCAAF player season stats are now reachable.
GET /v1/players/{id}/stats?sport=american_footballnow returns college football players' season stat lines (passing, rushing, receiving, and more), grouped by season with alevel: "season"label so you always know you are reading season aggregates, not a per-game log. This surfaces a large body of NCAAF season data that was in the store but had no discoverable path to it. -
Consistent sport and league names across the API. Sport and league naming is now resolved through one canonical registry, so the same name and aliases work everywhere.
GET /v1/sportsnow returns all nine covered sports (cricket, MMA, boxing, and Formula 1 are now visible alongside the rest), and unknown values like?sport=rugbynow return a clean400instead of an empty result. If you were special-casing name variants on your side, you can rely on the canonical names and aliases now.
2026-07-13
verificationis now evidence-based — the values changed. OnGET /v1/odds/historicalandGET /v1/odds/closing-lines, the per-lineverificationfield previously returnedtwo_source_verifiedwhenever we had no divergence on record. That was wrong: no divergence row usually means we never compared that line, not that two providers agreed — so a badge could appear on a line we never actually checked. It now returns one of four honest statuses:two_source_agreed(we compared two providers and they matched — backed by a real comparison record),two_source_diverged(we compared and they disagreed, withdivergence_categories; this replacesdivergence_flagged),single_source(only one provider carries this book/side, or the book has no cross-provider match — not verified), andunverified(we have not compared this line — not verified). If you were treatingtwo_source_verifiedas "trusted", note that most of those lines now correctly reportsingle_sourceorunverified; onlytwo_source_agreedis a positive cross-check. Documented at/docs/odds.
2026-07-09
-
Unknown player ids now return
404, not an empty200.GET /v1/players/{id}used to answer with a200andmeta.coverageset to false in two different situations: a player who exists but has no data for the requested sport yet, and an id that matches no player at all. The second case now returns a404with error codenot_foundand asuggested_fixthat points you at/v1/players?name=to look the player up by name. A real player who simply has no data for the requested sport still returns the same200withmeta.coveragefalse as before, because that is a legitimate answer and not an error. This is a small error-contract change: if you were treating an empty200on this endpoint as "not found", you can now rely on the status code instead. -
World Cup knockout fixtures now consolidated to one row each.
GET /v1/wc2026/matchescould previously return two rows for a single knockout game, with odds and scores split across them. Each knockout fixture is now a single canonical row carrying its full odds history, correct teams, and final score, so match and bracket reads are complete and consistent from the Round of 16 onward. -
GET /v1/stored-matches/:idnow returns complete current odds. The endpoint reads current lines from a dedicated latest-odds table, removing the previous 500-row cap and reporting the correct bookmaker count. The head-to-head field is also corrected for h2h matchups. -
Mobile layout fixes on the marketing site. The navbar and hero no longer cause horizontal scrolling on small screens, and code samples now scroll within their own blocks instead of stretching the page.
2026-06-28
- World Cup bracket odds are faster and no longer time out under load.
GET /v1/wc2026/matchesreports the current moneyline for all 104 fixtures. It used to compute that from the full append-only odds history (tens of millions of rows), which during a burst of live writes in the Round of 32 could run past two minutes and fail with a500. Current odds now read from a compact table that holds one row per match, market, book, and side, refreshed as each new line is written. The endpoint returns in well under 100ms and stays up while odds are streaming in fast. The response shape is unchanged, and the line-movement and historical odds endpoints still read the full history, so nothing there changed. - Paused API keys now return a clear
403. If we pause a key, which is different from revoking it, requests get a403with error codeKEY_PAUSEDand a plain-language message, so you can tell a temporary pause apart from a revoke or a quota429. It takes effect within the 60-second key cache. Most keys will never see this; it exists so that on the rare occasion we pause a key, the response says exactly what happened instead of failing in a confusing way.
2026-06-27
- Golden Boot leaderboard now returns real scorers.
GET /v1/wc2026/golden-bootwas returning an emptyleaders[]even with matches played, because it read a topscorers blob whose ingest job was never scheduled. It now aggregates the top 20 scorers from per-match fixture player stats (the table that is actually refreshed daily). Players tied on goals share a rank (DENSE_RANK), and the list is ordered by goals, then assists, then fewest minutes. Each leader carriescountry(the national team's full name, for example "Argentina", not an ISO code),team_id/team_name, a nullableplayer_id, andgoals/assists/minutes. See/docs/wc2026.
2026-06-26
- World Cup knockout rounds now read from the result, not the calendar.
GET /v1/wc2026/matcheswas labeling each knockout match'sbracket.roundby inferring it from the kickoff date. That broke whenever the schedule shifted. The handler now reads the authoritative FIFA stage stored on the match, so theroundvalue is correct regardless of date. Possible values:R32,R16,QF,SF,THIRD(third-place playoff),FINAL. Same change also filters out a small number of phantom placeholder rows that could appear before the group stage resolves. If you were mapping rounds yourself from dates, you can stop and trustbracket.round. - Dashboard quota card v2. The free-tier usage card on your dashboard now shows a circular gauge of today's percentage of the daily cap, with four bands (under 50% is just the number, 50-79% a soft notice, 80-99% a warning, and at/over the cap a clear "throttled until 00:00 UTC" state). Every band shows your real usage against the next tier's real cap and price, so the upgrade math is explicit, not a nag. Dashboard only; no API change.
- World Cup 2026 player data is flowing reliably. The WC2026 ingest worker (the pipeline behind tournament player and team data) is running stable ahead of the Round of 32. No endpoint or shape change; this is an operational note that the WC2026 player surfaces are kept current as the tournament progresses.
2026-06-21
- Abuse protection: per-key 4xx circuit breaker. Each API key now has a circuit breaker that watches its client-error (
4xx) rate, separate from the request quota. Over a rolling 5-minute window it trips if a key sends 500 client-error responses, or if 80% or more of at least 50 requests are4xx. On a trip the key gets429for a 120-second cooldown, even if it has quota left. This is abuse protection, not a punitive throttle: successful traffic never counts toward it, so normal usage is unaffected, and it clears itself once the broken requests stop. A breaker429carriesRetry-After,X-RateLimit-4xx-Cooldown, andX-RateLimit-4xx-Resetheaders so you can tell it apart from a quota429(which carries neither). If you see it, read theerror.messageandsuggested_fixon your4xxresponses and fix the request rather than blindly retrying. Documented at/docs/rate-limits.
2026-06-20
- Soccer team match statistics. Every finished match now carries a full per-team stat line: possession, shots (on/off target, total, blocked, inside/outside box), fouls, corners, offsides, cards, goalkeeper saves, passes (total, accurate, pass %), and expected goals (xG) where the competition provides it. Fetched from the upstream feed within ~30 minutes of full-time and stored, so reads are fast with no upstream wait. Available on
GET /v1/matches/{id}/statistics(Pro plan) for the top-five European leagues, MLS, and the Champions League. The linescore stays authoritative for goals; this fills the team stat line that match previews, recaps, and models need. - Odds now tell you how verified they are. Every line on the historical and closing-line endpoints (
GET /v1/odds/historical,GET /v1/odds/closing-lines) now carries averificationfield.two_source_verifiedmeans both of our odds providers priced that book, market, and side and they agree.single_sourcemeans only one provider priced it (or the book has no cross-provider match).divergence_flaggedmeans the two providers disagree, and adivergence_categorieslist tells you why (PRICE_DIFF,LINE_DIFF,MISSING_FROM_ONE,STALE). It is the two-source cross-check made visible on every line, so you can see at a glance which numbers earned two independent confirmations and which to treat with caution. Theverificationfield is live on both endpoints, andGET /v1/odds/divergences(Pro plan) returns the current disagreements directly. Documented at/docs/odds.
2026-06-19
- Two-source odds verification. We now run a cross-source check every 15 minutes, comparing our two independent odds providers for the same match, market, and book across the verified overlap books (BetMGM, DraftKings, FanDuel, Bovada, BetOnline, Lowvig) and flagging where they disagree. Four categories:
PRICE_DIFF(the juice differs),LINE_DIFF(the line itself differs),MISSING_FROM_ONE(only one provider carries it), andSTALE(one feed is lagging). Surfaced viaGET /v1/odds/divergences(Pro plan). Game lines only. This is the Honesty Architecture principle as a feature: it catches stale lines, upstream errors, and sharp-money asymmetries before they reach you.
2026-06-18
- Historical odds, opening line to closing line. We are now capturing historical odds snapshots for completed matches: the opening line (about a week before kickoff), the 24-hour line, and the closing line, each stamped with the board's as-of time (
snapshot_typeandsnapshot_aton odds data). Coverage reaches back to 2020 and is rolling out across the major sports (NBA, NHL, MLB, NFL, and the top soccer competitions). This is the raw material for closing-line value, line-movement analysis, and sharp-money signals. - Closing lines as games start. Phase 2 captures the closing line for upcoming games as they near kickoff: a 5-minute cron snapshots the current board in the roughly 10-minute window before the start as the closing line (
snapshot_typeclosing,snapshot_at= capture time). Closing-line coverage grows continuously from here, not only through backfill, which is exactly what closing-line value (CLV), line-movement intelligence, and sharp-money detection depend on. - Current odds across 17 sports and leagues. Phase 3 polls the live odds board every 30 minutes for NFL, NCAAF, NBA, NCAAB, WNBA, MLB, NHL, the top European soccer leagues (EPL, UEFA Champions League, La Liga, Bundesliga, Serie A, Ligue 1), MLS, MMA, and ATP/WTA tennis. This broadens current-odds coverage well beyond the World Cup, on a roughly 30-minute refresh.
- Query historical odds and closing lines. Two endpoints are now live:
GET /v1/odds/historical(Edge plan) returns every snapshot for a match (opening, 24-hour, closing) for closing-line-value work, andGET /v1/odds/closing-lines(Edge plan) returns closing lines across a sport and date range. Each odds row carries the snapshot type, the as-of time, and the sportsbook.
2026-06-17
- World Cup matches endpoint stays lean under load.
GET /v1/wc2026/matchesnow loads only the latest line per book and participant when it refreshes (about 3,200 rows instead of the full multi-million-row odds history), so a burst of traffic no longer spikes memory during the recompute. Same response shape, same odds. - Clearer errors on bad input. A malformed
?date=onGET /v1/wc2026/matchesnow returns a clear 400 that names the expected format instead of failing in a confusing way, and every sport-filter validation error across the API now carries asuggested_fixtelling you exactly what to change. - We measure before we claim. Last week we added a database index to speed up that matches query. Under real production load the planner ignored it, so it carried a write cost without the promised speedup. We removed it and reframed the work as the memory fix above, which is what actually held up. We would rather correct a claim than keep a number that does not survive a real query plan.
2026-06-16
- World Cup event data is complete.
GET /v1/matches/{id}/eventsnow reads across both the soccer and World Cup event tables in a single query, so goals, cards, and substitutions come back for World Cup 2026 matches (including the Stanley Cup and knockout fixtures that previously returned an empty list), not just the league tables it covered before. - Champions League and MLS scores route correctly.
GET /v1/matches?league=ucland?league=mlsnow resolve to the right upstream instead of returning nothing or Premier League data. UCL and MLS fixtures are queryable through the unified matches endpoint, the same way the European leagues already are. - Playoff series state on NHL and NBA matches. Match responses now carry a
seriesblock for postseason games (game number in the series, per-side wins, and the leader), so you can render "Game 6, Carolina leads 3-2" straight from the match payload without stitching the series together yourself. Live and upcoming games are accurate now; a fix for finished-game series state on the match-detail route is in progress. - Self-service errors on 36 more endpoints. The
suggested_fixfield, which lands on a 4xx when the fix is unambiguous, now covers 36 additional response sites across the API. More malformed requests now tell you exactly what to change, no support ticket required. - No more slow World Cup cold starts.
GET /v1/wc2026/matchesandGET /v1/wc2026/standingsnow serve from cache instantly and refresh in the background (stale-while-revalidate). A cache expiry no longer makes one unlucky caller wait several seconds for a recompute, so tail latency on these endpoints is gone. - Endpoint response contracts documented. A new reference spells out the exact response shapes for
GET /v1/wc2026/matchesandGET /v1/predictions: the{ total, matches }envelope (not a bare array), the knockout placeholder rows and how to detect them, the Solo-plan requirement on predictions, and the client timeout we recommend. Read it before you parse either response. - World Cup per-player tournament stats.
GET /v1/wc2026/players/{id}/tournamentreturns a player's aggregated tournament totals (goals, assists, cards, minutes) with a per-match event breakdown.
2026-06-12
- Top scorers by league.
GET /v1/leagues/{id}/top-scorersreturns the scoring leaders for the Premier League, La Liga, Bundesliga, Serie A, and Ligue 1, with goals, assists, minutes, and matches per player. Sort with?category=goals|assists|minutes|matches. Team names resolve through a direct join, so every row carries a clean club name. - Kickoff time on predictions.
GET /v1/predictionsresponses now includekickoff_utc. You no longer need a second call to the schedule endpoint just to get the match time. - World Cup matches are cache-hardened.
GET /v1/wc2026/matchesno longer returns an empty list during a deploy or a brief upstream hiccup. It never caches an empty response, serves the last known-good snapshot when a refresh comes back empty, and warms on boot. The intermittent empty results some callers saw around launch-day deploys are gone.
2026-06-11
- FIFA World Cup 2026 is live. Every fixture across groups A through L, with the schedule, live group standings, match predictions, value picks, odds, and Elo ratings. Start at /world-cup-2026-api.
- World Cup scores land at full-time. Final scores now populate within a cron tick of the final whistle, usually a minute or two after the match ends. The scores worker now reads the upstream soccer linescore shape correctly.
- Consistent post-match data. The post-match pipeline (events, Elo, standings, grading, and group advancement) now reads scores from a single source of truth,
matches.linescore. Those surfaces no longer disagree in the minutes right after a result lands. - Errors that help you fix them. Every 4xx and 5xx now returns a
suggested_fix(when the fix is unambiguous), adocs_url, and arequest_id, so you can self-serve most integration mistakes without opening a ticket.
2026-06-10
- Soccer player club form.
GET /v1/players/{id}/club-formreturns a player's club season stats (appearances, goals, assists, minutes, rating) across the top 5 European leagues (EPL, La Liga, Bundesliga, Ligue 1, Serie A) for 2025-26. 3,853 players covered, and WC2026 squad players link automatically. - Multi-season club form. Add
?season=2024to scope club form to a single prior season for career context. Omit it for every captured season. - NHL 2025-26 season complete. All 1,389 NHL matches this season, including all four Stanley Cup Final games, are now fully linked and queryable end to end.
2026-06-09
- NBA shot chart API. The shot chart page is live at /nba-shot-chart-api. Pull any player's 2025-26 shots with court X/Y coordinates, shot type, and make/miss via
GET /v1/players/{id}/shots(235k+ shots and counting). - NFL and soccer API pages enriched. Added query-targeted FAQs to the NFL and soccer API landing pages, answering the integration questions developers search for most.
- Security hardening. Authentication redirect validation tightened.
2026-06-08
- WC2026 live group standings.
GET /v1/wc2026/groupsreturns all 12 groups (A-L), 4 teams each, updated within 5 minutes of each matchday result. Group advancement probabilities (Monte Carlo, Elo-driven Poisson model, 10k sims) populate from June 11. The Monte Carlo model is calibrated against 301 WC matches (2002-2022): G=2.73 goals/match, C=186 Elo/goal. - Player trophy history.
GET /v1/players/{id}/trophiesreturns a football player's career honours from the upstream football database, with a top-leveltotal_trophieswin count. Cristiano Ronaldo has 54 titles, Lionel Messi 65. Football-only; non-soccer players return an empty list. - Team playoff records.
GET /v1/teams/{id}/playoffsreturns an NBA team's playoff record by season (the Boston Celtics went 16-3 in 2023-24). - Player transfer history.
GET /v1/players/{id}/transfersreturns a football player's club moves with fees. Kylian Mbappé: Monaco to PSG for €180M, then to Real Madrid on a free. - NBA shot coordinates.
GET /v1/players/{id}/shotsreturns a player's shot log with courtcoordinate_x/coordinate_y,shot_type, and made/miss. 235k+ shots from 2025-26 including the Finals. Free throws are excluded by default (?include_ft=trueto keep them). Build a shot chart or a shooting heat map straight from the JSON.
2026-06-07
- NBA player game log.
GET /v1/players/{id}/gamelogreturns flat per-game NBA stat lines, most recent first (last 20 games by default, up to 50). Filter bypostseasonandseason. - WC2026 squads.
GET /v1/wc2026/teams/{id}/squadreturns a nation's full 26-player squad, ordered by position group (goalkeepers, defenders, midfielders, forwards) then shirt number. - National team World Cup history.
GET /v1/teams/{id}/world-cup-historyreturns a nation's tournament-by-tournament record from 2002 through 2022: finish, games played, win/draw/loss, and goals for/against. - Team Elo history.
GET /v1/teams/{id}/elo/historyreturns a team's Elo trend line, a baseline seed plus one point per settled match, so the rating grows as matches play out. Optionalfrom/todate filters. - NBA player career averages.
GET /v1/players/{id}/careerreturns season-by-season career averages (7 NBA seasons, 2019-20 through 2025-26), one row per season and team. NHL players get the hockey equivalent. - WC2026 line movement.
GET /v1/wc2026/matches/{id}/line-movementreturns the chronological odds history per market and book, so you can chart how a line moved.
2026-06-06
- TypeScript SDK published.
@bigballsports/[email protected]is live on npm:pnpm install @bigballsports/sdkornpm install @bigballsports/sdk. One import, every sport. Superseded by@bigballsdata/[email protected](2026-07-24) — install@bigballsdata/sdkinstead.@bigballsports/[email protected]had a wrong default host and a broken install and is no longer maintained. - Developer blog live. The WC2026 API launch guide is at /blog/wc2026-api-launch: what's live, what's coming, and 3 example calls to get started.
- NHL historical standings captured. Past-season NHL standings for 2021 through 2024 (128 rows) now load into the canonical store, the groundwork for season-over-season standings comparisons.
- NHL season averages from raw box scores. Per-player NHL season averages (4,861 rows) are now computed directly from the captured raw box scores, so the matchup's per-player season numbers stay current without leaning on a stale upstream stats feed.
- WC2026 prediction calibration and value-finder persistence. A grader now scores the Elo match predictions against actual results to track calibration, and value-finder opportunities are written to storage (57 predictions persisted so far) instead of recomputed on every request.
- WC2026 knockout bracket fully structured. All 32 knockout fixtures from
GET /v1/wc2026/matchesnow carry abracketblock withround,home_feeder, andaway_feeder. Projected team names stay suppressed until the groups resolve; the feeder description shows instead (for example "Winner Group A"). - WC2026 squad data ingested. All 48 World Cup squads (1,248 players) are loaded, the data behind the upcoming
GET /v1/wc2026/teams/{id}/squadendpoint. - WC2026 team tournament history computed. 141 tournament records across the 2002 through 2022 World Cups are now in the store (France champions in 2018, Argentina in 2022), for historical context on each nation.
- Historical season guard. Requesting a past season the API does not hold (for example
?season=2023) now returns an explicit error instead of silently falling back to current-season data.
2026-06-05
- World Cup 2026 odds are live.
GET /v1/odds?tournament=wc2026returns moneyline, handicap, and totals for all 104 WC2026 fixtures. Mexico vs South Africa (the June 11 opener) has lines now; the remaining 103 matches populate as sportsbooks open their markets. - NHL goalie stats split by game type. The matchup now exposes separate regular-season and playoff save percentages for each goalie. Frederik Andersen sits at .931 across 12 Stanley Cup Final games, Carter Hart at .924 across 16 Western Conference Final games.
- NBA active rosters live. All 30 NBA teams are seeded in
roster_memberships(530 players, 2025-26), so the NBA matchup builds lineups from the authoritative active roster instead of the historical player pool. - Games endpoint accepts date ranges.
GET /v1/games?sport=nba&date=recentreturns the last 7 days of finished games in one call, anddate=YYYY-MM-DD:YYYY-MM-DDrange queries are supported up to 31 days.
2026-06-04
- NBA starting lineups live. Starters and positions for all 1,291 games of the 2025-26 NBA season, including playoffs, via
GET /v1/nba/games/{id}/lineups. - NBA playoff player data fixed. Lineup and box score gaps for postseason games (Conference Finals, NBA Finals) are resolved via direct BDL game bridging. All playoff games are now queryable via the lineups and stats endpoints.
- Matchup endpoints shielded from cache stampede.
GET /v1/nba/games/{id}/matchupandGET /v1/nhl/games/{id}/matchupnow collapse concurrent cache-miss recomputes into a single database query, so a burst of requests at the cache-expiry boundary no longer piles onto Postgres. - Play-by-play groundwork. NBA and NHL play-by-play events (including shot coordinates) now ingest into the canonical store, the foundation for upcoming shot-quality and possession analytics. No public endpoint yet.
2026-06-03
- World Cup 2026 knockout bracket is queryable.
GET /v1/wc2026/matchesnow returns all 104 fixtures, the 72 group matches plus the full Round of 32 through Final bracket. Knockout matches carry null teams until the group stage assigns them after June 28. - NHL matchup rosters come from the roster store.
GET /v1/nhl/games/{id}/matchupnow builds active rosters from the newroster_membershipstable instead of inferring from recent box scores, so full squads render (the Vegas Golden Knights roster is complete again). - Player-prop value finder.
GET /v1/intelligence/value-propsflags player props where the empirical hit rate beats the de-vigged market line.window=upcoming(default) covers the next 24 hours;window=allruns a historical backtest with record, ROI, and average edge. Markets: points, rebounds, assists, points_rebounds_assists, threes.
2026-06-02
- NBA Finals ingested. New York Knicks vs San Antonio Spurs, with the full series schedule loaded and Game 1 on June 3. Scores and
GET /v1/nba/games/{id}/matchupresolve for every game of the series. - NHL playoff series object.
GET /v1/nhl/games/{id}/matchupnow returnsmatch.stageplus aseriesblock: the game number in the series, per-side wins from finished games, and a human-readableseries_record(for example "NYR leads 2-1" or "Series tied 1-1"). It is null on regular-season and preseason matchups. - Postseason games staged correctly. Every NBA and NHL postseason game now carries the right
stagetag (playoffsvsregular), so playoff fixtures are no longer mislabeled as regular season on live pages or in the matchup series block. - NHL player props live, with hit rates. 5,168 prop rows loaded across 5 sportsbooks and 6 markets: shots_on_goal, goals, assists, points, blocked_shots, and power_play_points.
GET /v1/nhl/players/{id}/props?market=...returns the current line across books (best book per side) plus a rolling last-N-games hit rate for that market. Hit rates resolve across both player-id twins, so they fill in regardless of which id you start from. - NBA player props backfilled. 14,373 historical prop rows loaded across 9 sportsbooks, giving
GET /v1/nba/players/{id}/propsdeeper line history behind its hit rates. - Player injuries live for NBA and NHL. 127 NBA and 102 NHL players currently on the report, refreshed every 30 minutes.
GET /v1/injuries?sport=basketball|ice_hockeyreturns the full league list (one row per player, deduplicated) andGET /v1/players/{id}/injuryreturns a single player's status, injury type, expected return date, and note. Both join on the sameplayer_idyou use everywhere else. - Matchup responses now carry injury status.
GET /v1/nba/games/{id}/matchupandGET /v1/nhl/games/{id}/matchupsurface each player's current injury status inline, so the preview and the health check arrive in one call. - NHL box scores, captured in full. Per-game NHL skater logs keep backfilling into the canonical store (this is what powers the new prop hit rates), and the ingest now captures the complete raw box-score payload (all 37 fields the upstream returns), not just the subset surfaced today. Storage is cheap; missing data is permanent.
- NBA advanced, general, and clutch stats captured. Per-season NBA general, advanced, and clutch stat splits are now loading into the canonical store, the groundwork for richer NBA player and team analytics.
- Standings refresh within 60 seconds of a final whistle, across all 11 leagues. An event-driven worker picks up the
match:finishedsignal and re-ingests standings on the next 30-second tick, down from a daily cron that could lag up to 24 hours. - World Cup 2026 knockout bracket loaded. All 32 knockout fixtures (Round of 32 through the Final) are in the schedule alongside the 72 group matches, and
GET /v1/wc2026/matchesnow returns the full 104-match bracket. Knockout matches carry null teams until the group stage assigns them. - World Cup 2026 tournament leaders.
GET /v1/wc2026/leadersreturns the top scorers (rank, goals, assists, and games played per player) with an explicittournament_state:pre_tournamentbefore the June 11 opener, thenin_progress, thenfinished. - World Cup 2026 weather live for all 16 stadiums.
GET /v1/matches/{id}/weatherreturns the match-time forecast (temperature, precipitation, wind, condition) for every WC2026 venue. - NHL trending players.
GET /v1/intelligence/trending?sport=nhlnow surfaces hot and cold NHL players by goals, assists, and shots, mirroring the NBA trending signal. - Three more sportsbooks. Rebet, BetParx, and Betway are now in the books reference, so their lines surface in odds and props alongside the existing books.
- Provenance labels corrected. The
meta.sourcefield on the weather and golden-boot endpoints now reports accurate provenance. - Seven new sport-level API pages. Dedicated landing pages for basketball, hockey, baseball, American football, cricket, boxing, and Formula 1, each documenting the sport's endpoints and cross-linking to the underlying league pages.
- Data-health monitoring. An hourly job now checks that marquee data is present and correctly tagged: a championship game exists during each title window, no game is mislabeled regular-season inside a postseason window, and high-stakes games are not single-sourced. Gaps surface to us before they reach a live page.
- Roster history groundwork. A new
roster_membershipstable records player-to-team membership over time, the foundation for accurate historical rosters and lineups.
2026-06-01
- NHL Nerd-view reaches NBA parity.
/v1/nhl/games/{id}/matchupnow surfaces the same depth as the NBA composite: multi-book moneyline odds per sportsbook, team Elo ratings, goalie save percentage, penalty kill percentage, season statistical leaders via the sibling/v1/nhl/leadersendpoint, and a goalie depth chart. The underlying Elo seed runs across every NHL team, so/v1/teams/{id}/eloand the value-finder answer for ice_hockey today. - NHL standings fix. Vegas Golden Knights now returns the correct
VGKshort_name across/v1/standings?league=nhl, team profiles, and every downstream intelligence endpoint that joins on team identity. Corrected at the database so consumers see consistent Vegas data without a client-side patch. - NHL standings deduplicated.
/v1/standings?league=nhlno longer surfaces duplicate team rows. Ten team rows that carried stale duplicates were corrected at the database; downstream consumers see one row per franchise. - Homepage coverage matrix caught up. The Ice Hockey Predictions cell on the homepage matrix flipped from "Coming soon" to "Live" to reflect today's NHL Elo seed shipment. Five other stale cells were audited and filed for a separate accuracy sweep.
- WC2026 hero countdown. /world-cup-2026-api shows a live days/hours/minutes ticker counting down to the June 11 opener, refreshes every minute, collapses to "Tournament live now" once kickoff lands.
- WC2026 Golden Boot endpoint.
GET /v1/wc2026/golden-bootreturns the tournament-wide top-scorers leaderboard with explicit lifecycle state:pre_tournamentbefore June 11 (leaders empty),in_progressduring the tournament,finishedafter the final. - Live win probability endpoint.
GET /v1/wc2026/matches/{id}/live-probabilityreturns pre-match Elo baseline, live-adjusted probability, and percentage-point delta per outcome. 15-second cache. - Per-sport coverage index at /docs/sports. New page listing every supported sport with its coverage level (Live, Historical, Partial), three to five key endpoints, and a link to the per-sport landing page. Useful for picking a starting point when integrating a new sport.
- NHL Player Props teaser at /nhl-props-api. Endpoint shipping for the Stanley Cup Finals. The page documents the planned shape (five markets: shots_on_goal, goals, assists, points, blocked_shots), the cross-book aggregation contract, and lets agents integrate against the schema before launch.
- WC2026 Soccer Props teaser at /soccer-props-api. Player props for the World Cup 2026 opener on June 11. Four markets at launch (anytime_goalscorer, to_score_2plus, assists, shots_on_target), books post these 24-48 hours before each fixture, and the page uses competition-scoped hit rates rather than misleading season denominators.
- MLB Statcast teaser at /mlb-statcast-api. 2.1 million pitches loaded across the 2023, 2024, and 2025 seasons. Barrel rate, xwOBA, exit velocity, launch angle, and bat tracking (2024+) live in the database today; sprint speed and OAA pending. Public endpoint in development.
- The competitor calculator now reflects real tier pricing. The Compare/calculator page (/compare/api-request-calculator) models a competitor's actual seven-tier plan structure (Free through Max, $0 through $2,499/mo) with per-second rate, monthly data-points quota, real-time vs delay, and WebSocket availability shown as chips on the competitor card. Calculator picks the cheapest fit against both peak rate and monthly cap.
- NHL landing page now links to the props teaser via a new FAQ entry, and
/v1/nhl/leadersjoins the staticEndpoints list to match the EndpointExplorer entry. - MLB landing page retracts the stale "pitch-by-pitch granularity is not currently exposed" claim. The updated FAQ now references the 2.1M-pitch Statcast load and points at the /mlb-statcast-api teaser. Added a Statcast research card to buildIdeas.
- NHL EndpointExplorer caught up. Added
/v1/nhl/leadersentry (full field tree with rank, player, team, ice time, goals/assists/points/shots, xG, goals above xG). Expanded the matchup field tree with the three new fields shipped today:books[]for multi-book moneyline,season_stats.pk_pctfor penalty kill percentage, andplayers[].season_avg.save_pctfor goalie save percentage. - This page is live. /whats-new is the granular feed of recent improvements. Bookmark it. New entries land at the top whenever something ships.
2026-05-31
- UCL + MLS live data shipped.
/v1/standings?league=cl|mlsand/v1/matches?sport=football&league=cl|mlsnow return real data via the upstream live-data partner (were misrouting to Premier League or returning empty). - Champions League and MLS API pages live. /champions-league-api and /mls-api now surface 6 entries each: DB-backed fixture archive plus live standings plus live matches.
- Pre-tournament form populated.
/v1/wc2026/matches/{id}.pre_match_formis now filled in BEFORE kickoff (was empty until ~2h pre-kickoff). Last 5 fixtures per team computed from the upstream football data partner. /v1/matchesseason auto-defaults per league family. European-season leagues (cl, epl, laliga, bundesliga, serie_a, ligue1) and calendar-year leagues (mls) get sensible defaults. Override with?season=YYYY./v1/matchespagination now works.?pageand?limitapply as a post-filter slice. Previously silently ignored./v1/matches/{id}/eventsshape fix. Response now correctly keyeddata.events(wasdata.stats). 16-second coverage:false latency also resolved.- Footer API Status badge. Green health dot plus "API Status" label in the marketing footer, linking to the live UptimeRobot status page.
- Task #80 closed. CPV vs KSA kickoff time confirmed stable at
2026-06-27T00:00:00.000Z. - Auth resilience. Malformed API keys fast-reject before I/O. Legacy
bbs_live_<48char>key format accepted alongside the modern<slug>_<hex>format.
2026-05-30
- NFL EndpointExplorer. /nfl-api now ships an interactive 7-entry explorer covering schedules, plays (with EPA + Win Probability), and weekly player stats.
- MLB EndpointExplorer. /mlb-api ships a 10-entry interactive explorer.
- WC2026 group advancement.
/v1/wc2026/group/{A-L}/advancement(per-team 1st/2nd/3rd/4th probability) surfaced in the WC2026 explorer. - Match weather endpoint covers all 16 WC2026 stadiums via Open-Meteo. Kickoff temperature, precipitation, wind, and normalised condition.
- NHL xG loaded. Per-skater season summary loaded (Dataset #36). Goalie GSAx tier branch added.
- Single-flight recompute prevents Postgres stampede at the cache TTL boundary on
/v1/wc2026/standingsand/v1/wc2026/matches.
2026-05-29
- Standalone team Elo endpoint.
GET /v1/teams/{id}/eloreturns current Elo rating + league rank. Bets Chat Intel tab integration ready. - Value-finder shipped.
GET /v1/intelligence/value-finderreturns model-edge value opportunities across odds markets (Elo-implied probability vs market-implied, configurable edge threshold). - Quality tier endpoint.
GET /v1/players/{id}/tier(1-6 scale with overall + position percentile). NBA + NHL coverage today. - EndpointExplorer rolled out to /nba-api, /nhl-api, and the five European soccer league pages.
- Soccer xG ingest. Soccer player tiers now wired off xG90.
- /status redirect. Internal
/statusnow redirects to the public UptimeRobot page. - X-Cache header on four composite endpoints so consumers can verify cache hits.
2026-05-28
- WC2026 group advancement endpoint shipped.
GET /v1/wc2026/group/{group}/advancement— per-team finishing-position probability via 10,000-iteration Monte Carlo. - NBA leaders.
GET /v1/nba/leadersreturns statistical leaders with team affiliation. - NBA hot/cold movers.
GET /v1/intelligence/trendingranks players by last-5 vs season average. - NBA player situational splits.
GET /v1/nba/players/{id}/splits. - Player game-log + rolling-stats.
GET /v1/players/{id}/game-logand/v1/players/{id}/rolling-stats. - Player season projection.
GET /v1/players/{id}/season-projection(82-game pace). - Player vs-team head-to-head.
GET /v1/players/{id}/vs-team/{team_id}. - Player streak.
GET /v1/players/{id}/streak(hot/cold form). - NHL matchup composite endpoint.
GET /v1/nhl/games/{id}/matchup. - 17 new league + sport API landing pages.
- API Request Calculator on the homepage and at /compare/api-request-calculator.
- WC2026 match weather.
GET /v1/matches/{id}/weathervia Open-Meteo. - NBA team Elo from box scores. Internal Elo store now driven from box-score totals (Task #28).