Get Coverage
Public data-coverage report, per league, over trailing 7-day and 30-day windows ending today, denominated by SCHEDULED games (never games with odds, which would flatter coverage). Every registry league is included. No auth: this feeds the public /accuracy page. Rate-limited (see below).
Per league, per window:
- two-plus-books share: fraction of scheduled games where the main markets (ML/Spread/Totals) drew >=2 distinct bettable bookmakers.
- median books per market (median_books.ml / .spread / .totals): for EACH main market separately, the median across the window's scheduled games of the count of distinct bettable bookmakers carrying a usable two-sided quote for THAT market. Each market gets its own median rather than one figure unioned across ML, Spread, and Totals, so a game with rich moneyline coverage can never mask a bare spread or totals market the way a single combined number would. A scheduled game with zero fetched rows for a market counts as 0 books in that market's median, not as an excluded game.
- freshness (games_stale / stale_share): for a COMPLETED game (kickoff has passed), age is measured AT KICKOFF: the last pre-kickoff quote's source_fetched_at (T1.2), compared to the game's own kickoff time. This is deliberate. The kickoff-freeze gate means market_lines stops being touched once a game starts, so measuring a finished game's freshness against query time (now) would call every finished game stale purely because time has passed since kickoff. The "last pre-kickoff quote" is itself bounded to source_fetched_at <= kickoff in the SQL, so this stays correct even if the freeze gate's behavior ever changes; it can never read a post-kickoff write as if it were the pre-kickoff quote. For an UPCOMING game, age is query-relative: source_fetched_at compared to now. A game is "stale" if that age exceeds 30 minutes, or if it has no fetched bettable quote at all (missing data counts against coverage, it is not excluded).
- outage days: count of days in the window that had >=1 scheduled game for this league but zero games with a fresh (non-stale) quote that day. This intentionally reuses the same 30-minute freshness rule as games_stale, which is stricter than "the league had any fetch at all that day": a day with a fetch that was simply too old still counts as an outage day. That means this figure can only ever over-report outage days relative to a looser definition, never under-report one.
Every share is null, not zero, when its window has zero scheduled games for that league (0/0 is undefined). Vendor last-update timestamps are not available to us; see freshness_note on the response, which the /accuracy page must surface verbatim ("as of our last fetch", not the book's own time).
Cached in-process for 15 minutes behind one shared key (the endpoint takes no parameters) with a singleflight lock, so concurrent cold requests share one recomputation across all leagues instead of each re-running it. Rate-limited to 30 requests/hour per IP (mirrors the public transparency calibration route's posture), so even the cheap cache-hit path stays bounded for an anonymous caller; the singleflight lock already bounds concurrent recomputation, so this is the only gap the rate limit closes. Pass/fail thresholds for billing purposes are not computed here; they are preregistered separately in docs/product/product_thesis.md.
Request
- Base URL: https://api.fairline.io (the document declares no server; this is the origin it was published from)
- URL: https://api.fairline.io/api/coverage
- Auth: none declared
Response
Successful Response
Changes
No recorded changes to this operation across all 1 revision of this API.