Skip to content
On this page

API Endpoints

This is the full customer-facing REST API reference for Scottfree Sports.

Production base URL:

text
https://sports-api.scottfreellc.com

All /api/v1/* endpoints require an API key unless explicitly marked public. The production OpenAPI schema is also available at:

text
https://sports-api.scottfreellc.com/openapi.json

Common Path Values

Sports:

CodeLeague
mlbMajor League Baseball
nbaNational Basketball Association
nflNational Football League
nhlNational Hockey League
ncaafNCAA Football
ncaabNCAA Basketball

Model types:

Model typeMarketPositive sideNegative side
over_underTotalOverUnder
won_on_pointsMoneylineHome teamAway team
won_on_spreadSpreadHome spreadAway spread

Prediction model columns:

ColumnMeaning
IMPLIEDMarket-implied no-vig probability for the positive side
AIAI model probability, when present in the prediction artifact
BLENDEnsemble blend probability
CATBCatBoost
RFRandom Forest
LGBLightGBM
LOGRLogistic Regression
XGBXGBoost
XTExtra Trees

For moneyline and spread, probabilities are home-team relative. For over/under, probabilities are over-relative.

Authentication

Use the X-API-Key header:

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/sports

Bearer auth also works:

bash
curl -H "Authorization: Bearer $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/sports

Query-string auth works but is not recommended for production because keys can appear in logs:

bash
curl "https://sports-api.scottfreellc.com/api/v1/sports?api_key=$SFS_API_KEY"

Root And Health

GET /

Unauthenticated service root. Useful only for a quick connectivity check.

bash
curl https://sports-api.scottfreellc.com/

Response shape:

json
{
  "message": "AlphaPy Sports API",
  "version": "1.0.0",
  "docs": "Contact support for documentation",
  "security_scan": "disabled",
  "deployment_test": "ready"
}

GET /api/v1/health

Unauthenticated health check for production monitoring. Includes dependency status for Firestore, Redis, and GCS.

bash
curl https://sports-api.scottfreellc.com/api/v1/health

GET /api/v1/readiness

Unauthenticated readiness check used by Cloud Run.

bash
curl https://sports-api.scottfreellc.com/api/v1/readiness

GET /api/v1/liveness

Unauthenticated liveness check used by Cloud Run.

bash
curl https://sports-api.scottfreellc.com/api/v1/liveness

GET /api/v1/data-status/{sport}

Unauthenticated freshness check for the daily model artifacts. Use this endpoint, not the general health endpoint, to learn whether a sport's predictions, results, summaries, and model tracking for today are being served and how current they are.

operational means the site is serving today's slate for the sport. Freshness problems are reported separately through status and degradation_reason while the served slate stays available; only a missing slate makes operational false.

bash
curl https://sports-api.scottfreellc.com/api/v1/data-status/mlb

The daily deadline is 10:15 America/New_York. operational remains true during the normal morning update window and on confirmed off-days. After the deadline it is false if the run is missing, stale, incomplete, or storage cannot be checked.

StatusMeaningoperational
pendingToday's run has not started and the deadline has not passed.true
updatingSome of today's artifacts are present before the deadline.true
freshToday's active-slate run and all expected artifacts are complete.true
off_dayToday's run completed and confirmed that the sport has no slate.true
partialOnly part of today's data was published after the deadline.false
staleToday's run or artifacts are missing after the deadline.false
unknownStorage returned no trustworthy freshness signal.false

Predictions are rebuilt after every two-hour odds update. Between updates the served slate stays fresh; the sport and each market report refresh_pending: true and the market reports a refresh_deadline one odds cycle after the newer odds arrived. A refresh that is still pending at that deadline reports status: stale with degradation_reason: predictions_stale, and the late market reports partial; the served slate remains available and operational stays true. fresh therefore means today's complete slate is served, not that the newest odds have already been applied.

degradation_reason values: source_stale (no verified odds update within its window), predictions_stale (a refresh missed its cycle), results_awaiting_settlement (results not yet settled against the newest source), daily_output_stale (today's slate is missing after the deadline), release_missing and release_unavailable (nothing verifiable can be served).

The markets object reports the publication time for each prediction, result, summary, and model-tracking artifact, the release id, the source time the forecast was built from (source_as_of), and any held games. A held game keeps its previous prediction and is listed for operators; it never changes the market or sport status. These are artifact timestamps; they do not fall back to the API response time.

Discovery

GET /api/v1/sports

Returns supported sports and model types.

Tier: Scottfree Sports AI.

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/sports

Response:

json
{
  "sports": [
    {"code": "mlb", "name": "Major League Baseball"},
    {"code": "nba", "name": "National Basketball Association"},
    {"code": "ncaab", "name": "NCAA Basketball"},
    {"code": "ncaaf", "name": "NCAA Football"},
    {"code": "nfl", "name": "National Football League"},
    {"code": "nhl", "name": "National Hockey League"}
  ],
  "model_types": [
    {"code": "over_under", "name": "Over/Under"},
    {"code": "won_on_points", "name": "Moneyline"},
    {"code": "won_on_spread", "name": "Point Spread"}
  ]
}

Predictions

GET /api/v1/predictions/{sport}/{model_type}

Returns model probabilities and market line fields for the current slate. Games are sorted by date and ET game time.

Tier: Scottfree Sports AI.

Query parameters:

NameTypeDefaultDescription
upcoming_onlybooleantrueWhen true, excludes games before today's ET slate. If today's model run has not completed yet, the endpoint keeps the latest valid slate visible instead of disappearing at midnight. Set false to include the whole prediction artifact.

Examples:

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  "https://sports-api.scottfreellc.com/api/v1/predictions/mlb/won_on_points"

curl -H "X-API-Key: $SFS_API_KEY" \
  "https://sports-api.scottfreellc.com/api/v1/predictions/nba/won_on_spread?upcoming_only=false"

Response shape:

json
{
  "sport": "mlb",
  "model_type": "won_on_points",
  "predictions_date": "2026-05-23T14:12:05.000000",
  "games": [
    {
      "date": "2026-05-23",
      "time": "19:10:00",
      "away_team": "new_york_yankees",
      "home_team": "colorado_rockies",
      "predictions": {
        "IMPLIED": 0.3741,
        "BLEND": 0.4112,
        "XGB": 0.3929,
        "RF": 0.4255
      },
      "away_money_line": -135,
      "home_money_line": 115,
      "open_away_money_line": -128,
      "open_home_money_line": 108,
      "ev_away_ml": -3.1,
      "ev_home_ml": 1.8
    }
  ],
  "models_used": ["IMPLIED", "AI", "BLEND", "CATB", "RF", "LGB", "LOGR", "XGB", "XT"]
}

Market-specific fields:

Model typeCurrent line fieldsOpening line fieldsEV fields
over_underover_under, over_line, under_lineopen_over_underev_pos, ev_neg
won_on_spreadaway_point_spread, home_point_spread, away_point_spread_line, home_point_spread_lineopen_away_point_spread, open_home_point_spreadev_away_spread, ev_home_spread
won_on_pointsaway_money_line, home_money_lineopen_away_money_line, open_home_money_lineev_away_ml, ev_home_ml

EV fields are informational calculations from model probability and market price. They are not a validated betting selector and should not be treated as a guaranteed edge.

Public ML Predictions

GET /api/v1/public/ml-picks/{sport}/{market}

Public, unauthenticated endpoint used by the free website ML Predictions page. This is intentionally limited: it shows free model probabilities and current market line context, but it excludes account tooling, API key data, MCP/CLI, and paid app workflows.

Markets:

Public marketBacking model type
over_underover_under
moneylinewon_on_points
spreadwon_on_spread

Query parameters:

NameTypeDefaultDescription
upcoming_onlybooleantrueSame current-slate behavior as the authenticated predictions endpoint.

Example:

bash
curl "https://sports-api.scottfreellc.com/api/v1/public/ml-picks/mlb/moneyline"

Response shape:

json
{
  "sport": "mlb",
  "market": "moneyline",
  "model_type": "won_on_points",
  "predictions_date": "2026-05-23T14:12:05.000000",
  "games": [
    {
      "date": "2026-05-23",
      "time": "19:10:00",
      "away_team": "New York Yankees",
      "home_team": "Colorado Rockies",
      "pick": "Colorado Rockies ML",
      "pick_side": "home",
      "blend_probability": 0.4112,
      "confidence": 0.1776,
      "market_line": "ML -135/+115",
      "implied_probability": 0.3741,
      "lines": {
        "away_money_line": -135,
        "home_money_line": 115
      },
      "models": {
        "BLEND": 0.4112,
        "XGB": 0.3929
      }
    }
  ],
  "models_used": ["BLEND", "XGB", "RF", "LGB", "CATB", "LOGR", "XT"],
  "note": "Free ML predictions only. App workflows, MCP/CLI tools, and account features require a paid Scottfree Sports plan."
}

Odds

GET /api/v1/odds/{sport}

Returns current game odds for a sport.

Tier: Scottfree Sports AI.

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/odds/mlb

Response shape:

json
{
  "sport": "mlb",
  "date": "2026-05-23T16:00:00.000000",
  "games": [
    {
      "match_id": "abc123",
      "date": "2026-05-23",
      "time": "19:10:00",
      "away_team": "new_york_yankees",
      "home_team": "colorado_rockies",
      "away_point_spread": -1.5,
      "away_point_spread_line": 145,
      "home_point_spread": 1.5,
      "home_point_spread_line": -165,
      "away_money_line": -135,
      "home_money_line": 115,
      "over_under": 10.5,
      "over_line": -110,
      "under_line": -110,
      "last_updated": "2026-05-23 15:55:00"
    }
  ],
  "last_updated": "2026-05-23T16:00:00.000000"
}

Prediction Markets

GET /api/v1/markets/{sport}

Returns a per-game game-winner probability comparison for the sport's current slate: our model's win probability, the sportsbook's no-vig implied probability, and the Kalshi prediction-market price, side by side. Useful for spotting where the model, the book, and the betting markets disagree.

Tier: any authenticated tier.

Notes:

  • Game-winner (moneyline) only — prediction markets don't carry spread or total markets.
  • Prices are live (short server-side cache). Only games that haven't started yet carry a Kalshi price; in-progress games and games with no matching market return null for the kalshi_* fields.
  • Kalshi coverage is currently MLB, NBA, NFL, and NHL. Other sports return rows with null Kalshi values.
bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/markets/mlb

Response shape:

json
{
  "sport": "mlb",
  "as_of": "2026-06-17T13:49:00.000000",
  "games": [
    {
      "date": "2026-06-17",
      "time": "19:10",
      "away_team": "cleveland_guardians",
      "home_team": "milwaukee_brewers",
      "model_home": 0.56,
      "model_away": 0.44,
      "book_home": 0.5817,
      "book_away": 0.4183,
      "kalshi_home": 0.56,
      "kalshi_away": 0.42,
      "edge_home": 0.0
    }
  ]
}

Field notes:

  • model_* is our blended model win probability; book_* is the no-vig implied probability from the moneyline; kalshi_* is the Kalshi market price (0–1). All are home/away win probabilities.
  • edge_home is the model's home probability minus the best available market price (book or Kalshi) — a quick read on model/market disagreement.

Results

GET /api/v1/results/{sport}/{model_type}

Returns graded results from the season archive: one row per game, with the final score, the market's recorded line and prices, every model's probability (BLEND, IMPLIED, CATB, RF, LGB, LOGR, XGB, XT) and the quote and settlement provenance fields. The same rows, filter and columns are served with or without season; only the order and the paging differ.

  • Without season: the current season (the latest season with a graded regular-season or postseason game; between seasons, the season just finished), newest game first, with plain offset paging. season in the response names it.
  • With season: that season, oldest game first, with snapshot paging (echo the first page's snapshot on later pages; a changed snapshot returns 409).

Professional leagues include regular-season and postseason games only. The summary and model tracking count exactly these rows, by the same rule, so a reader who regrades the rows reproduces both.

Tier: Scottfree Sports AI.

Query parameters:

NameTypeDefaultDescription
limitinteger100Maximum result rows to return.
seasonstring—Season label, such as 2024 for MLB or 2024-25 for split-year sports. Call GET /api/v1/results/{sport}/{model_type}/seasons for valid values. Omitted: the current season, newest first.
offsetinteger0First row of the page.
snapshotstring—With season, the first page's snapshot, on every later page.

Example:

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  "https://sports-api.scottfreellc.com/api/v1/results/mlb/won_on_points?limit=25"

A whole season (check next_offset and echo snapshot):

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  "https://sports-api.scottfreellc.com/api/v1/results/mlb/won_on_points?season=2024&limit=5000"

How each row is graded (the rule the summary and tracking share):

  • graded is true when at least one model graded the game; otherwise ungraded_reason says why: no_final (no final score), no_line (the market posted no line for this market), push (the final landed exactly on the line), no_prediction (no model took a side).
  • push is true for a final exactly on the line, false for a decided game, null without a final or line. The outcome columns (over, won_on_spread, won_on_points) are binary and read a push as an under or a non-cover, so leave ungraded rows out when counting hits.
  • A model's pick is the side its probability favors: above 0.5 the home or over side, below 0.5 the away or under side, exactly 0.5 no pick. IMPLIED is the no-vig probability of both recorded prices, whatever their quote_source; a game priced on one side only makes no IMPLIED pick.
  • model_probability_source says where a probability came from: recorded before the game, or walk_forward (reconstructed out of sample for games played before the live record began). Model tracking's provenance counts each kind.

Every season-store row says where its forecast came from:

FieldMeaning
forecast_sourcelive: the probabilities the pipeline recorded before the game started. walk_forward: component probabilities reconstructed by a walk-forward backtest fold trained on earlier seasons; a BLEND the row already held is kept. Absent: the game has no prediction.
forecast_runFor live, the id of the forecast release that produced the standing forecast (the release manifest is the publication record). For walk_forward, the backtest run id.
forecast_published_atFor live, when the standing forecast was produced, in UTC: the last forecast before the start, since a started game's forecast is never rewritten. Absent for walk_forward rows, which were never published before the game, and for live rows recorded before 2026-10-03 whose publication no retained release records.

For live rows recorded before 2026-10-03 the pipeline did not stamp the forecast; forecast_run and forecast_published_at were backfilled from the retained forecast releases: the last release published before the game's start whose BLEND equals the archived one. A live row with no such release keeps forecast_source: live with the other two fields absent.

The season store is the ledger of the season: a game enters it with its first published forecast, before it is played (no scores, no outcome, push null), and settlement adds the final.

Response shape:

json
{
  "sport": "mlb",
  "model_type": "won_on_points",
  "season": "2026",
  "results": [
    {
      "date": "2026-10-01",
      "time_est": "20:08:00",
      "season": "2026",
      "away_team": "new_york_yankees",
      "home_team": "colorado_rockies",
      "away_score": 4.0,
      "home_score": 2.0,
      "home_money_line": 115.0,
      "away_money_line": -135.0,
      "open_home_money_line": 108.0,
      "won_on_points": 0.0,
      "BLEND": 0.4112,
      "IMPLIED": 0.3741,
      "CATB": 0.44,
      "RF": 0.41,
      "LGB": 0.39,
      "LOGR": 0.42,
      "XGB": 0.40,
      "XT": 0.43,
      "quote_source": "jsonodds",
      "settlement_status": "settled",
      "push": false,
      "graded": true,
      "ungraded_reason": null
    }
  ],
  "last_updated": "2026-10-03T19:06:20.876446Z",
  "total": 2419,
  "returned": 25,
  "offset": 0,
  "next_offset": 25,
  "snapshot": null,
  "source_start": "2026-03-25",
  "source_end": "2026-10-01"
}

total counts every row of the season, graded or not; model tracking's archive_games is the same number.

GET /api/v1/results/{sport}/{model_type}/rolling

Returns 4-week rolling result curves, one per archived season, computed from the same graded games /results serves (one row per game; a push is left out of the win counts). Used by the Results page visualizations.

Tier: Scottfree Sports AI.

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/results/nfl/won_on_spread/rolling

Response includes:

FieldDescription
sportSport code
model_typeModel type
seasonsSeason-level rolling curves
historical_averageAverage curve across included seasons, when generated
last_updatedPublication time of the newest season archive the curves were computed from

GET /api/v1/results/{sport}/{model_type}/density

Returns result-density grids (the current season against the previous ten) computed from the same graded games /results serves. Used by Results page charts.

Tier: Scottfree Sports AI.

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/results/nba/over_under/density

Response includes the generated chart payload and last_updated.

GET /api/v1/results/{sport}/{model_type}/team-form

Returns per-team form for the current season from the season archive: the same graded games /results serves, so a team's record here is the record a reader regrading those rows gets. A push is no result for either team. next_game_date comes from the game-score tape.

Tier: Scottfree Sports AI.

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/results/mlb/won_on_points/team-form

Response shape:

json
{
  "sport": "mlb",
  "model_type": "won_on_points",
  "current_season": "2026",
  "season_end": "2026-05-23",
  "last_n": 20,
  "teams": [
    {
      "team": "new_york_yankees",
      "games": 20,
      "wins": 12,
      "losses": 8,
      "win_pct": 0.6,
      "last_10": [1, 0, 1, 1, 0, 1, 0, 1, 1, 0],
      "streak": "W1"
    }
  ],
  "last_updated": "2026-05-23T16:00:00.000000"
}

GET /api/v1/results/{sport}/{model_type}/seasons

Lists the archived seasons for a sport and market, newest first, and says which one is current. current_season is the season /results, /summary and /models/.../tracking describe when no season is given: the latest with a game played against a posted line, which between seasons is the one just finished. live is true while that season is in progress (a decided game in the last 14 days); live_season is the label while live and null between seasons.

Tier: Scottfree Sports AI.

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/results/mlb/won_on_points/seasons

Response shape:

json
{
  "seasons": ["2026", "2025", "2024", "2023"],
  "current_season": "2026",
  "live": true,
  "live_season": "2026"
}

Strategies

GET /api/v1/strategies/{sport}/{model_type}

Returns the betting-strategy leaderboard for a sport and market — the same ranked track records (ROI, win%, record, sample size) shown on the Strategies page in the web app (this page was previously labeled "Summary," then "Models"). Sixteen strategies are evaluated: eight pure (Follow/Fade × the line-movement and model-edge signals) and eight hybrids that combine both signals.

A strategy is a simple, mechanical betting rule built from two signals:

  • Line Move — how the line moved from the opener to the current price (e.g. the line moved toward the home team).
  • Model Edge — the gap between our model's probability (BLEND) and the market-implied probability (IMPLIED).

For each signal you either Follow it (bet the side the signal points to) or Fade it (bet the other side). Hybrid strategies require a Line Move and a Model Edge agreeing. Each strategy's leaderboard entry is its season-to-date record applying that rule to every game.

This is distinct from /summary, which reports per-model performance. /strategies ranks betting rules over the line-move and model-edge signals.

Tier: Scottfree Sports AI.

Query parameters:

ParameterDescription
seasonOptional. e.g. 2026 (MLB) or 2025-26 (NFL/NBA/NHL/NCAAF/NCAAB). Defaults to the latest season in the store. Call GET /api/v1/results/{sport}/{model_type}/seasons for the valid values and the current-season label.
bash
curl -H "X-API-Key: $SFS_API_KEY" \
  "https://sports-api.scottfreellc.com/api/v1/strategies/mlb/won_on_points?season=2026"

Response shape:

json
{
  "sport": "mlb",
  "market": "won_on_points",
  "season": "2026",
  "min_sample": 30,
  "last_updated": "2026-06-17T13:49:00.000000",
  "strategies": [
    {
      "id": "hybrid-pb-pb",
      "name": "Follow +Move AND Follow +Edge",
      "signal": "Hybrid",
      "rank": 1,
      "wins": 153,
      "losses": 107,
      "record": "153-107",
      "win_pct": 58.8,
      "roi": 12.7,
      "n": 260,
      "ranked": true
    }
  ]
}

Field notes:

  • Without accounting, deprecated legacy_v1 uses recorded moneylines and assumed -110 spreads/totals.
  • Request accounting=recorded_v2 for recorded-price performance. Records are W-L-P; won is null for pushes. Stakes include pushes, win percentage excludes pushes, and ranking requires 30 decisive bets. Missing prices or unverified finality are excluded from monetary metrics and counted in exclusions. No eligible bets means null ROI, not zero ROI. Replay optionally accepts include_excluded=true for per-game reasons.
  • rank is the 1-based position among ranked strategies, ordered by ROI descending. It is null for low-sample strategies (ranked: false), which are returned at the end ordered by n descending.
  • ranked is true only when n >= min_sample (30). Below that the ROI is too noisy to rank.
  • signal is the strategy family: Line (line movement), Δ Edge (model − implied), or Hybrid.

To track how strategies evolve over a season, poll this endpoint over time (it reflects the season-to-date record each day) and store the snapshots.

Summary

GET /api/v1/summary/{sport}/{model_type}

Returns per-model performance for the current season, computed from the same results archive rows /results serves and /models/.../tracking grades, by the same rule: a game grades with a final and a line the market posted; a push (a final exactly on the line) grades no model; a model picks the side its probability favors and exactly 50% is no pick; IMPLIED picks from the no-vig probability of both recorded prices, and a game priced on one side only makes no IMPLIED pick; ENSEMBLE picks when five or more of the six individual models agree. A model with no graded pick this season is not listed. season names the season; grading repeats these rules.

Fields per model:

FieldMeaning
total_games, wins, lossesGames the model picked and the pick's result. Pushes and games with no pick are not counted.
win %, fade %wins / total_games and its complement.
win % 4WThe same over the trailing 28 days ending on the latest decided game.
over % / under % (or home % / away %)Win rate when the model picked that side; 1W and 4W variants over the trailing 7 and 28 days.
ev roiFor every graded game with the model's probability and both recorded prices: bet one unit on the side with the higher expected value at those prices; a push returns the stake; ev roi is the mean return per game. 1W and 4W variants; ev roi season equals ev roi.
bt bets, bt wins, bt losses, bt unitsThe same system taking only bets whose expected value is at least +10%: bets placed, won, lost, and net units; a push counts as a bet with zero units. 4W variants; the season-suffixed fields equal the headline fields.

Windows anchor to the latest decided game of the season, not the wall clock. IMPLIED and ENSEMBLE have no probability of their own, so their ev roi is null and their bt * fields are zero.

Tier: Scottfree Sports AI.

NameTypeDefaultDescription
seasonstring—Season label (2024, 2024-25). Omitted: the current season. Any archived season is summarized by the same rule from its archive rows.
bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/summary/mlb/won_on_points
curl -H "X-API-Key: $SFS_API_KEY" \
  "https://sports-api.scottfreellc.com/api/v1/summary/nhl/over_under?season=2024-25"

Response shape:

json
{
  "sport": "mlb",
  "model_type": "won_on_points",
  "season": "2026",
  "summaries": [
    {
      "model": "BLEND",
      "total_games": 2417,
      "wins": 1338,
      "losses": 1079,
      "win %": 0.554,
      "win % 4W": 0.531,
      "fade %": 0.446,
      "home %": 0.581,
      "away %": 0.502,
      "home % 1W": 0.6,
      "away % 1W": 0.5,
      "ev roi": -0.0213,
      "bt bets": 412,
      "bt wins": 190,
      "bt losses": 219,
      "bt units": -12.4
    }
  ],
  "grading": {"pushes": "excluded: a final exactly on the line grades neither a hit nor a miss", "...": "..."},
  "generated_date": "2026-10-03T19:06:20.876446Z"
}

generated_date is the publication time of the archive the summary was computed from, the same last_updated /results reports.

Summary labels are market-relative:

Model typePositive labelNegative label
over_underoverunder
won_on_pointshomeaway
won_on_spreadhomeaway

Model Tracking

GET /api/v1/models/{sport}/{model_type}/tracking

Per-model prediction accuracy: a snapshot per window (season, last 4 weeks, last week), a weekly rolling-4W series, cumulative excess hits against the IMPLIED market baseline, calibration, consensus groups and recent games. Computed from the season archive at read time by the same grading rule /results and /summary use, for the current season and for any archived season alike, so a model's n here is its total_games in the summary for that season.

Tier: Scottfree Sports AI.

NameTypeDefaultDescription
seasonstring—Season label (2026, 2025-26). Omitted: the current season, the latest season with a graded game, the same season /results and /summary describe.
bash
curl -H "X-API-Key: $SFS_API_KEY" \
  "https://sports-api.scottfreellc.com/api/v1/models/nhl/over_under/tracking?season=2023-24"

How it is counted (also returned in grading):

  • A season grades exactly the games in its results archive, with the archive's final scores and prices. Preseason games and other seasons' games are excluded. first_game_date and last_game_date give the bounds; source is live, backtest (walk-forward) or backtest+live, and provenance counts each.
  • total_games is the games at least one model graded. ungraded counts every other row of the season's results archive by reason: pushes (a final exactly on the line, neither a hit nor a miss; also ungraded_pushes), no_line (the market posted no line), no_final, no_prediction (no model took a side). archive_games is the archive's row count, so archive_games == total_games + sum(ungraded) and equals /results total for the same season.
  • A model at exactly 50% makes no pick on that game, so a model's n can sit below total_games. Consensus counts only games where all six individual models vote and none sits at exactly 50%.
  • last_1w falls inside last_4w, and both inside season: overlapping views of the same games, not independent samples.

Response fields include sport, model, season, source, total_games, ungraded, ungraded_pushes, archive_games, grading, first_game_date, last_game_date, snapshot, weekly, cumulative, calibration, consensus_by_price, recent_games, provenance and data_integrity.

Customer Account

GET /api/v1/customers/me

Returns the current customer record for the API key.

Tier: Scottfree Sports AI. Any authenticated key can call this, but a customer without an active subscription may have api_plan: null or a zero API quota.

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/customers/me

Response shape:

json
{
  "customer_id": "cus_abc123",
  "email": "customer@example.com",
  "name": "Customer Name",
  "tier": "ai",
  "api_plan": "ai",
  "api_subscription_status": "active",
  "subscription_status": "active",
  "monthly_requests": 128,
  "monthly_limit": 200000,
  "created_at": "2026-05-20T17:11:00+00:00"
}

GET /api/v1/customers/me/usage

Returns current API quota state.

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/customers/me/usage

Response shape:

json
{
  "monthly_requests": 128,
  "monthly_limit": 200000,
  "requests_remaining": 199872,
  "tier": "ai",
  "api_plan": "ai",
  "api_subscription_status": "active",
  "subscription_status": "active",
  "reset_date": "2026-06-01T00:00:00+00:00"
}

GET /api/v1/customers/me/api-keys

Lists the current customer's API keys. Keys are masked in list responses.

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/customers/me/api-keys

Response shape:

json
[
  {
    "api_key": "sk_alphapysports_abcd*****",
    "name": "Default Key",
    "tier": "ai",
    "created_at": "2026-05-20T17:11:00+00:00",
    "is_active": true,
    "expires_at": null
  }
]

POST /api/v1/customers/me/api-keys

Creates a new API key for the current customer.

bash
curl -X POST \
  -H "X-API-Key: $SFS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Terminal"}' \
  https://sports-api.scottfreellc.com/api/v1/customers/me/api-keys

Response shape:

json
{
  "api_key": "sk_alphapysports_new_key_value",
  "message": "API key created successfully",
  "warning": "Store this key securely - it cannot be retrieved again"
}

PATCH /api/v1/customers/me/api-keys/rename

Renames one of the current customer's API keys. You may pass the full key or the masked value returned by GET /api/v1/customers/me/api-keys.

bash
curl -X PATCH \
  -H "X-API-Key: $SFS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"api_key_to_rename":"sk_alphapysports_abcd*****","new_name":"Production App"}' \
  https://sports-api.scottfreellc.com/api/v1/customers/me/api-keys/rename

Response shape:

json
{
  "message": "API key renamed successfully"
}

DELETE /api/v1/customers/me/api-keys/{api_key}

Revokes an API key. The API refuses to revoke the key currently being used for the request.

bash
curl -X DELETE \
  -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/customers/me/api-keys/sk_alphapysports_abcd*****

Response shape:

json
{
  "message": "API key revoked successfully"
}

Scottfree Sports Data

These endpoints support historical dataset refresh, included with every Scottfree Sports AI subscription. They let a customer refresh historical datasets they previously purchased from the shop (datasets themselves are one-time purchases, separate from the monthly subscription).

GET /api/v1/scottfree-sports-data/status

Returns the caller's historical dataset entitlement state. This does not consume a refresh.

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/scottfree-sports-data/status

Response shape:

json
{
  "has_scottfree_sports_data": true,
  "expires_at": "2026-06-13T00:00:00+00:00",
  "owned_products": ["nfl_historical"],
  "owned_sports": ["nfl"],
  "refreshes_used": 1,
  "refreshes_remaining": 7,
  "refresh_limit": 8,
  "next_reset_at": "2026-06-01T00:00:00+00:00"
}

POST /api/v1/scottfree-sports-data/refresh

Generates signed download URLs for every refreshable historical dataset the customer owns. This consumes one of the customer's 8 monthly historical dataset refreshes only after all URLs are successfully generated.

bash
curl -X POST \
  -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/scottfree-sports-data/refresh

Response shape:

json
{
  "refreshes_remaining": 7,
  "next_reset_at": "2026-06-01T00:00:00+00:00",
  "downloads": [
    {
      "sport": "nfl",
      "filename": "nfl_game_scores_1g.csv",
      "url": "https://storage.googleapis.com/...",
      "expires_at": "2026-05-23T16:15:00+00:00"
    }
  ]
}

The signed URLs expire after 15 minutes.

Compatibility Pick Endpoints

These endpoints exist so older clients do not break, but no validated betting selector is active. They return explicit empty payloads.

GET /api/v1/picks/today

bash
curl -H "X-API-Key: $SFS_API_KEY" \
  https://sports-api.scottfreellc.com/api/v1/picks/today

Response:

json
{
  "date": "2026-05-23",
  "picks": [],
  "summary": {"total_picks": 0, "by_sport": {}},
  "note": "No validated betting selector is active."
}

GET /api/v1/picks/{date}

Same empty payload shape as /picks/today, for a supplied YYYY-MM-DD date.

GET /api/v1/picks/performance

Optional query parameters:

NameValues
sportMLB, NBA, NFL, NHL, NCAAF, NCAAB
marketspread, total, moneyline

Returns zeroed performance windows with the note No validated betting selector is active.

GET /api/v1/edge-picks

Public-compatible endpoint for edge-pick experiments. It currently returns an empty payload.

Optional query parameters:

NameDescription
sportAccepted for compatibility; ignored while no selector is active.

GET /api/v1/edge-picks/{date_str}

Same empty edge-picks payload for a supplied date.

GET /api/v1/edge-picks/track-record

Returns an empty track record while no selector is active.

Errors

The API error handler returns this shape:

json
{
  "error": "HTTP_401",
  "detail": "Invalid or expired API key",
  "timestamp": "2026-05-23T16:00:00.000000"
}

Common statuses:

StatusMeaning
400Invalid request or invalid parameter
401Missing, invalid, expired, or revoked API key
403Valid key, but the plan does not allow that operation
404Data artifact not available for that sport/model/date
429Per-second or monthly quota exceeded
500Server error
503Dependency temporarily unavailable

Sports model data and research tools