← 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 PPR projections under seasons.2026.season_projections. The MISC_FPTS field is the projected total fantasy points (for K and DST it's FPTS).

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, "RUSHING_YDS": 1380, ... }
    }
  }
}

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}

def points(p):
    sp = p["seasons"]["2026"]["season_projections"]
    return float(sp.get("MISC_FPTS", sp.get("FPTS", 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