Build an NBA prop model.
Three endpoints and real fields: train on graded lines, add recent form from game logs, and score today’s board. The base rates to beat are in the panel above.

Base rates to beat, NBA graded lines
| Market | Standard over rate | Standard rows | All tier rows |
|---|---|---|---|
| Points | 43.9% | 726 | 5,417 |
| Pts+Rebs | 46.0% | 695 | 5,042 |
| Pts+Rebs+Asts | 44.4% | 723 | 4,633 |
| Rebounds | 47.9% | 422 | 4,037 |
| Pts+Asts | 46.2% | 563 | 4,011 |
| Rebs+Asts | 49.7% | 459 | 3,643 |
| Assists | 44.8% | 281 | 3,026 |
| 3-PT Made | 53.9% | 165 | 2,753 |
Games from 2026-10-28 to 2026-11-02. Pushes excluded. A short sample, so expect these to move. Every market and player
Step 1: pull the graded lines to train on
Start from the training set: one flat row per graded NBA line, with the features known before the game and the result. Drop the pushes, keep the rows where the before-game features are filled in, and split features from label.
import os, requests
API = "https://prizepicksapi.com/api/v1"
H = {"Authorization": f"Bearer {os.environ['PRIZEPICKS_API_KEY']}"}
rows = requests.get(f"{API}/training-set", headers=H, params={
"league": "NBA", "tier": "standard", "limit": 1000,
}).json()["data"]
rows = [r for r in rows if r["result"] != "push"]
rows = [r for r in rows if r["rest_days"] is not None and r["home_away"] is not None]
X = [[r["closing_line"], r["rest_days"], r["home_away"] == "home"] for r in rows]
y = [r["result"] == "over" for r in rows]Step 2: add recent form from game logs
The line alone is weak. Add the player’s recent box scores as features, read before the game you are scoring. Game logs are full-game box scores; a stat missing from stats was not reported and is not zero, so handle absent keys rather than defaulting them.
logs = requests.get(f"{API}/game-logs", headers=H, params={
"player": "Jayson Tatum", "league": "NBA", "last": 10,
}).json()["data"]
# Average only the games before the one you are scoring; never the same game.
pts = [g["stats"].get("points") for g in logs if g.get("minutes")]Step 3: score today’s board
Fetch the current board for the market, build the same features for each line, and compare your estimate with the line. Read observed_at and only act on lines that are still current.
board = requests.get(f"{API}/projections", headers=H, params={
"league": "NBA", "stat": "Points", "view": "compact", "limit": 1000,
}).json()["data"]Avoid the two mistakes that ruin a model
- Leakage: minutes, actual_value and result are known only after the game. Use them as labels or as past-game features, never as features of the same game.
- Survivorship: do not train only on lines that still exist today; that drops everything that went unavailable. Use the recorded history as of the decision time.
Try it
Endpoint docs:GET /api/v1/training-setGET /api/v1/game-logsPlans and limits
Frequently asked questions
What endpoints does an NBA prop model use?
Three: /api/v1/training-set for one graded row per line to train on, /api/v1/game-logs for recent-form features, and /api/v1/projections to score today’s board. /api/v1/opponent-stats adds the matchup.
Do I need a paid plan?
Free is enough to inspect fields and try a small baseline. Developer removes the training-row sample and ten-game log limits when the requested training window needs more data. Paying does not guarantee enough history for a reliable model.