Data for a PrizePicks prop model.

For developers choosing the data for a PrizePicks prop model: labeled lines, grades where available, game logs and matchup stats.

A prop model needs what the line was at the time you could have acted, and what actually happened. The API gives you both, keyed to the same projection, so you can build training rows without hand-joining box scores.

Not for

Multi-book closing-line models across many sportsbooks
A broad odds API has the breadth. Pair it with these labeled lines. Read more
Long-run line history
Recorded lines start 28 October 2026. Read more
Pixel-art magnifying glass examining a bar chart.

Base rates to beat, NBA graded lines

Graded lines45,126
Standard over rate45.6%
Goblin over rate64.4%
Demon over rate20.3%
MarketStandard over rateStandard rowsAll tier rows
Points43.9%7265,417
Pts+Rebs46.0%6955,042
Pts+Rebs+Asts44.4%7234,633
Rebounds47.9%4224,037
Pts+Asts46.2%5634,011
Rebs+Asts49.7%4593,643
Assists44.8%2813,026
3-PT Made53.9%1652,753

Games from 2026-10-28 to 2026-11-02. Pushes excluded. A short sample, so expect these to move. Every market and player

What one training row looks like

GET /api/v1/results returns one row per line. Use line as the target threshold: it is the last line observed before the start, and closing.gap_seconds says how long before. Treat actual_value and result as labels only.

FieldUse in a model
lineThe threshold you predict against, known before the game.
opening_lineFeature: how far the line moved before the start.
closing.gap_secondsFilter: drop rows where the last observation is too old for your use.
sidesThe tier on each side: standard, goblin or demon.
market.stat, player, event.opponentKeys to join your own features.
actual_value, result, minutesLabels. Never use these as features.

Pull every graded row with Python

This writes one CSV row per graded line, paging with the numeric cursor. It uses only the standard library, so it runs anywhere.

import csv, json, os, time
from urllib.parse import urlencode
from urllib.request import Request, urlopen

KEY = os.environ["PRIZEPICKS_API_KEY"]

def results(league="NBA"):
    cursor = None
    while True:
        params = {"league": league, "status": "graded", "limit": 200}
        if cursor:
            params["cursor"] = cursor
        req = Request("https://prizepicksapi.com/api/v1/results?" + urlencode(params),
                      headers={"Authorization": "Bearer " + KEY})
        with urlopen(req, timeout=30) as res:
            page = json.load(res)
        yield from page["data"]
        cursor = page["meta"]["next_cursor"]
        if not cursor:
            return
        time.sleep(1.1)  # stays under the Free plan's per-minute limit

with open("training.csv", "w", newline="") as f:
    out = csv.writer(f)
    out.writerow(["date", "player", "stat", "tier", "line", "opening_line", "moved", "actual", "over"])
    for r in results():
        if r["result"] not in ("over", "under"):
            continue  # pushes carry no over or under label
        moved = r["line"] - r["opening_line"] if r["opening_line"] is not None else ""
        out.writerow([r["game_date"], r["player"]["name"], r["market"]["stat"], r["sides"].get("more", {}).get("tier"),
                      r["line"], r["opening_line"], moved, r["actual_value"], int(r["result"] == "over")])

Keep the future out of your features

  • Split by date, never at random. Lines for the same game are correlated, and a random split leaks them across folds.
  • Build rolling player features only from games before game_date.
  • Use line and opening_line, not any value recorded after the start.
  • Report sample size per market and tier. Small markets swing hard.
  • Drop rows with a large closing.gap_seconds if you act close to the start.

Measure against the base rate first

The table above this guide is the number to beat. A model that predicts the over rate for each market and tier has real skill only if it improves on that baseline out of sample. Check calibration too: a model that says 60% should hit about 60% of the time.

Score the live board

GET /api/v1/projections returns the same player, market and line fields for the current board. Pass view=compact for up to 1,000 rows per page. Run your features for those rows, then keep the line timestamp with each prediction so you can grade it later against /api/v1/results.

curl -G "https://prizepicksapi.com/api/v1/projections" \
  -d league=NBA -d stat=Points -d view=compact -d limit=1000 \
  -H "X-User-Request: Read PrizePicks data for an integration check" -H "Authorization: Bearer $PRIZEPICKS_API_KEY"

Try it

Endpoint docs:GET /api/v1/resultsGET /api/v1/projections/{id}/historyPlans and limits

Frequently asked questions

What data do I need to train a PrizePicks prop model?

One row per line with the line at game time, the opening line, the tier, the market and the actual stat. GET /api/v1/results returns that per graded line, and your own features join on player, date and market.

Which leagues have graded results for modeling?

Graded results cover NBA full-game lines today. Every other league still has lines and full move history from the start of recording, so you can join your own box scores to them.

Should goblin and demon lines be modeled with standard lines?

Model them separately or carry the tier as a feature. Goblin and demon lines are more-only and sit away from the standard line, so a pooled hit rate mostly measures the tier mix.

Continue building

References