A fantasy football API for Python
Quick answer: Use requests with your key in the x-api-key header and call https://api.gridirondata.com/api/v1/projections/{week}. Responses are plain JSON lists that drop straight into pandas. A free key (50 requests a month, no card) covers player lookups and this week's projections; stats, snap share and news need Basic at $45 a year.
- Library
- requests
- Auth
- x-api-key header
- Format
- JSON
- Free plan
- 50 req/mo
How do I make my first call from Python?
Get a free key, then set it in your shell: export GRIDIRON_API_KEY=.... Install requests with pip install requests.
import os
import requests
API = "https://api.gridirondata.com/api/v1"
session = requests.Session()
session.headers["x-api-key"] = os.environ["GRIDIRON_API_KEY"]
r = session.get(f"{API}/projections/8", params={"position": "WR", "scoring": "half"}, timeout=10)
r.raise_for_status()
rows = sorted(r.json()["projections"], key=lambda p: p["projected_points"], reverse=True)
for p in rows[:10]:
print(f'{p["player_name"]:<22} {p["team"] or "":<4} {p["projected_points"]:>5.1f} {p["injury_status"]}')
Example output (numbers change every week):
Ja'Marr Chase CIN 18.9 ACTIVE
CeeDee Lamb DAL 17.5 ACTIVE
Puka Nacua LAR 0.0 OUT
Players who can't play this week (OUT, IR, bye) project 0; the row keeps projected_points_if_active. Scoring defaults to PPR; pass scoring=half or standard.
How do I handle plan and rate limits?
Limits answer with JSON and a machine-readable reason, so a small wrapper keeps errors readable:
def get(path, **params):
r = session.get(f"{API}{path}", params=params, timeout=10)
if r.status_code in (403, 429):
body = r.json()
# reason: basic_required, pro_required, free_monthly_quota, quota_exceeded, rate_limited
raise RuntimeError(f'{body.get("reason")}: {body.get("error")} {body.get("upgrade_url", "")}')
r.raise_for_status()
return r.json()
player = get("/players", name="CeeDee Lamb", position="WR")
print(player["player_id"], player["team"], player["espn_player_id"])
Player ids look like CeeDee Lamb#WR. URL-encode the space and the # when the id goes in a path.
Stats and snap share (Basic)
from urllib.parse import quote
pid = quote("CeeDee Lamb#WR", safe="") # -> CeeDee%20Lamb%23WR
stats = get(f"/stats/{pid}/2025") # game-by-game actuals
snaps = get(f"/snaps/{pid}", season=2026, trend=3)
print(stats["season_points"], snaps["season_snap_pct_avg"], snaps["trend_delta"])
Basic is $45 a year (or $5 a month) for 5,000 requests a month: actuals from 2020, snap share, deviations, matchups, news, rest-of-season projections, the schedule and byes.
Load it into pandas
import pandas as pd
df = pd.DataFrame(get("/projections/8", position="RB")["projections"])
print(df[["player_name", "team", "projected_points", "injury_status"]].head(12))
For historical play-by-play, the free nflreadpy package from nflverse is a good complement; join on player name and position.
What can I build next?
- A start/sit optimizer from
/projections/{week}. - A trade analyzer with rest-of-season value.
- Game-day injury alerts from the Pro changes feed.
- A prop pricer with
/props(Pro).
The full endpoint list is in the API reference and the OpenAPI spec, which also generates typed clients.
Frequently asked questions
Is there a pip package for GridIron Data?
Not on PyPI yet. The API is plain REST and JSON, so requests (or httpx) is all you need, and the OpenAPI spec at /openapi.json can generate a typed client.
How do I send the API key from Python?
Set the x-api-key header on a requests.Session: session.headers['x-api-key'] = os.environ['GRIDIRON_API_KEY']. The key is not accepted as a query parameter.
Is the Python example free to run?
Yes for the current week's projections and player lookups: the free plan is 50 requests a month with no card. Stats, snaps and news need Basic at $45 a year.
Does it work with pandas?
Yes. List endpoints return arrays of flat JSON objects, so pd.DataFrame(response['projections']) gives a table directly.