← All guides

Build a Draft Value (VORP) Tool

Stop drafting off someone else's cheat sheet. In ~30 lines of Python you'll rank every player by Value Over Replacement Player (VORP) using live 2026 PPR projections — tuned to your exact league.

What you'll build: a script that pulls projections for every position, computes each player's points above a "replacement-level" starter, and prints a draft board sorted by value across positions.

Endpoints used

1. Pull projections for a position

Every player profile carries season-long projections under seasons.2026.season_projections. MISC_FPTS is the projected total in PPR (the default), with MISC_FPTS_HALF and MISC_FPTS_STD for half-PPR and standard leagues (for K and DST the base field is FPTS, with matching _HALF / _STD). Pick the one that matches your league.

curl -H "x-api-key: YOUR_KEY" \
  "https://api.gridirondata.com/api/v1/players?position=RB"

Each player looks like this (trimmed):

{
  "player_id": "Bijan Robinson#RB",
  "player_name": "Bijan Robinson",
  "position": "RB",
  "seasons": {
    "2026": {
      "team": "ATL",
      "season_projections": {
        "MISC_FPTS": 373.3,        // PPR (default)
        "MISC_FPTS_HALF": 344.9,   // half-PPR
        "MISC_FPTS_STD": 316.4,    // standard
        "RUSHING_YDS": 1380, ...
      }
    }
  }
}

Prefer the API do the picking? GET /api/v1/projections/{week}?scoring=half returns weekly projections already resolved to your scoring (ppr default, or half / standard).

2. Why VORP beats raw points

A 280-point RB and a 280-point QB are not equal. There are far more startable QBs than RBs, so the RB is scarcer — and scarcity is what wins drafts. VORP measures each player against the last startable player at their position:

VORP = player_points − replacement_points[position]

"Replacement" is the player ranked at roughly the number of starters your league rosters at that position. For an 8-team league we use these ranks (tune to your league):

REPLACEMENT_RANK = {"QB": 14, "RB": 30, "WR": 30, "TE": 10, "K": 8, "DST": 8}

3. The full script

import requests

API = "https://api.gridirondata.com/api/v1"
KEY = "YOUR_KEY"
H = {"x-api-key": KEY}
REPLACEMENT_RANK = {"QB": 14, "RB": 30, "WR": 30, "TE": 10, "K": 8, "DST": 8}

# "ppr" (default), "half", or "std" — match your league.
SCORING = "ppr"
SUFFIX = {"ppr": "", "half": "_HALF", "std": "_STD"}[SCORING]

def points(p):
    sp = p["seasons"]["2026"]["season_projections"]
    base = "MISC_FPTS" if "MISC_FPTS" in sp else "FPTS"
    return float(sp.get(base + SUFFIX, sp.get(base, 0)))

# 1. Pull every position
players = []
for pos in REPLACEMENT_RANK:
    rows = requests.get(f"{API}/players", headers=H, params={"position": pos}).json()
    rows = rows.get("players", rows) if isinstance(rows, dict) else rows
    for p in rows:
        if p.get("seasons", {}).get("2026", {}).get("season_projections"):
            players.append({"id": p["player_id"], "pos": pos, "pts": points(p)})

# 2. Replacement baseline per position
repl = {}
for pos, rank in REPLACEMENT_RANK.items():
    ranked = sorted((p["pts"] for p in players if p["pos"] == pos), reverse=True)
    repl[pos] = ranked[min(rank - 1, len(ranked) - 1)]

# 3. VORP + sorted board
for p in players:
    p["vorp"] = round(p["pts"] - repl[p["pos"]], 1)

board = sorted(players, key=lambda x: -x["vorp"])
for i, p in enumerate(board[:24], 1):
    print(f"{i:2}. {p['id']:24} {p['pos']:3} proj {p['pts']:6.1f}  VORP {p['vorp']:6.1f}")
💡 Make it yours: blend in prior-season production with /api/v1/stats/{player_id}/2025 to fade injury-prone or unproven players, or change REPLACEMENT_RANK for a 10- or 12-team league. Add an OP/superflex slot and QB value jumps.

Next steps