INDEPENDENT DEVELOPER REFERENCE
Build a baseline NBA points prop model
Build a chronological NBA points baseline from recorded box scores, score it on later games and compare with Pick6 targets you recorded yourself.
Editorial review: · Not a live coverage report
This is a data engineering tutorial. It does not produce picks, and nothing here shows an edge, a win rate or a profit. No wagering.
What you will build
A baseline that predicts a player's points from the average of their own earlier games, scored on later games. It is deliberately simple. A rolling mean tells you what any later model must beat, and it runs on the small statistics samples available today.
Independent ESPN statistics cover NBA, NFL and NHL as limited retained game samples, not full seasons. As of 2026-11-03 those samples were captured after the games were played, so they do not show what was known at game time. Recorded Pick6 lines began around 2026-11-03, so this tutorial does not backtest against past seasons of lines. It cannot, because those lines were not recorded.
export DRAFTKINGS_API_KEY=...
python3 nba-points-baseline.py --season 2027 --min-games 5 --max-pages 5Pull NBA box scores
NBA seasons use their ending year: season=2027 is 2026-27 and season=2026 is 2025-26. Rows are JSON only, with limit up to 200 and cursor pagination through pagination.next. Use completed=true so in-progress games stay out.
curl --fail-with-body "https://draftkingsapi.com/api/v1/stats/player-games?sport=NBA&season=2027&completed=true&limit=200" \
-H "Authorization: Bearer $DRAFTKINGS_API_KEY"{
"data": {
"athlete_id": "ESPN_ID",
"name": "Player Name",
"category": "boxscore",
"did_not_play": false,
"stats": [
{ "key": "minutes", "label": "MIN", "scope": "game", "display": "14", "numeric": 14 },
{ "key": "points", "label": "PTS", "scope": "game", "display": "3", "numeric": 3 }
]
},
"game": { "starts_at": "START_TIME", "completed": true, "event_id": "ESPN_EVENT" }
}Check available and coverage in the response. An empty matching page returns available: false, which means no stored rows and does not mean the games did not happen.
Extract the target
For NBA rows, category is boxscore and the target is the stat with key: "points". Read its numeric value and require scope to be game. Skip rows where did_not_play is true or numeric is null rather than scoring them as zero.
def points(row):
if row["data"].get("did_not_play"):
return None
for stat in row["data"]["stats"]:
if stat["key"] == "points" and stat["scope"] == "game":
return stat["numeric"] # None when not a scalar
return NoneOrder by game time, then roll
Pagination order follows a cursor built from event and athlete IDs, not the calendar. Sort each athlete's games by game.starts_at before computing anything. The prediction for a game uses only earlier games.
from collections import defaultdict
history = defaultdict(list)
rows.sort(key=lambda r: r["game"]["starts_at"])
for row in rows:
value = points(row)
if value is None:
continue
earlier = history[row["data"]["athlete_id"]]
prediction = sum(earlier[-5:]) / len(earlier[-5:]) if len(earlier) >= MIN_GAMES else None
# record (prediction, value) here, before appending this game
earlier.append(value)Appending after predicting is what prevents leakage from the current game. Athletes with fewer than --min-games earlier games get no prediction, and the report counts them.
Score it on later games
Use a date cutoff. Fit nothing on games after it, and report mean absolute error on games after it. MAE reads in points, so it is easy to explain. Report the sample size next to it every time.
- Print the number of athletes and games scored, and how many were skipped and why.
- A small test window gives a noisy number. Do not quote it as model accuracy in general.
- Compare against a trivial baseline such as the player's previous game. A model that cannot beat it has not added anything.
The Python walkthrough covers caching, the cutoff and each piece of the script.
Compare to recorded Pick6 targets
Comparison needs a target you recorded before the game. A Pick6 row read today says nothing about a target last week. Record NBA Points rows, then compare after games finish. Fresh NBA rows depend on the slate, and the table on status shows capture age.
python3 pick6-board.py --sport NBA --stat Points --limit 25 --max-pages 2- Rows are market rows. One athlete can have several targets, so choose a rule, such as a row with both
MoreandLess, and state it. - There is no verified mapping from Pick6
entity_idto ESPNathlete_id. If you match on name, refuse ambiguous matches and report the rest. - Pick6 rows carry standings multipliers such as 1.5x. A standings multiplier is not decimal odds, a probability or an established entry payout.
- A difference between your prediction and a target is a number to inspect. It is not a recommendation, and a small sample cannot show an edge.
Try it
Check the recorded data
Questions
Can I backtest this model against past NBA seasons of lines?
No. Lines were not recorded for past seasons. You can score predictions against box scores in retained samples and compare to Pick6 targets only for rows you recorded before the game.
Which season value should I use for the NBA?
NBA uses the ending year. Use 2027 for the 2026-27 season and 2026 for 2025-26. A valid season does not promise full coverage, so check the available field.
Will this model make money?
This tutorial does not show that and is not betting advice. It builds a baseline for prediction error on a small sample. No edge, win rate or profit is established.
Why start with a rolling mean?
It is easy to verify, avoids leakage when ordered correctly and sets the bar for anything more complex. Current samples are too small to support an elaborate model.
Related resources
A walkthrough of a standard-library Python baseline: cursor pagination, numeric stat parsing, a SQLite cache and MAE on a later held-out window.
NBA game logs API for player statisticsBuild an NBA player game log from retained ESPN box scores: season years, stat keys, cursor pagination and why you must sort rows yourself.
NBA points props data for developersPair NBA Points rows from Pick6 with ESPN player points from the statistics API, and see where the join and the history stop.
Training data for sports prop modelsWhy there is no ready-made training set here, where point-in-time leakage comes from, and how to record your own as-of dataset from now on.
Independent service operated by DD Inc., not affiliated with or endorsed by DraftKings. Technical access is not a redistribution license. No wagering: this service does not place, recommend or settle entries or bets. Check sources and status for actual capture context.