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?

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.