API Documentation
Quick answer: The GridIron Data API is a REST API for fantasy football data at https://api.gridirondata.com/api/v1. You send your key in the x-api-key header and get JSON back. It covers players, projections, 2020–2026 actuals, deviations, snap share, news, matchups and the NFL schedule. Pro ($29/month) is the betting plan: prop probabilities with fair odds, defense vs position, betting lines, per-stat projections, and injury changes within minutes with webhooks. A free key allows 50 requests/month with no credit card for player lookups and this week's projections; the rest starts at Basic ($5/month).
How do I make my first call?
- Create a free account and confirm your email. No credit card is needed.
- Copy your API key from the dashboard.
- Send the key in the
x-api-keyheader on every request. - Cache responses: projections and stats refresh once a day.
curl -H "x-api-key: YOUR_API_KEY" \ "https://api.gridirondata.com/api/v1/players?name=Josh%20Allen&position=QB"
Using Python? Install the official client from GitHub. It reads GRIDIRON_API_KEY, retries server errors and raises clear exceptions for plan and rate limits. Client README →
pip install "git+https://github.com/gridirondata/gridiron-data-python"
from gridirondata import GridIron
gi = GridIron() # reads GRIDIRON_API_KEY
print(gi.find_player("Josh Allen", "QB"))
Need a key for YOUR_API_KEY? Get a free API key No credit card.
How do I authenticate?
All API requests require an API key passed in the
x-api-key header.
Send the header name in lowercase — x-api-key — for the widest client
compatibility.
curl -H "x-api-key: YOUR_API_KEY" \ "https://api.gridirondata.com/api/v1/health"
What are the rate limits?
Limits depend on your plan and apply per API key. See pricing for the full comparison.
| Plan | Requests | Per minute | Betting data and injury changes |
|---|---|---|---|
| Free ($0, no card) | 50 per month (resets on the 1st, 00:00 UTC) | 10 | No |
| Basic ($5/month) | 5,000 per month | 30 | No |
| Pro ($29/month) | 50,000 per month | 100 | Yes: /props, /defense, odds, per-stat projections, injury changes, webhooks |
| Enterprise | Custom | Custom | Yes |
Teaching a course? Students can get a Basic key free for the term through the Classroom program (setup guide).
What does the free plan cover?
A free key covers two things:
- Player lookups:
/players,/players/searchand/players/{player_id}. On the free plan a player record is the profile only, for the current season: name, position, team, injury status and jersey number. Weekly stats, snaps, projections and matchup history are left out of the record, and afree_planfield says so. - This week's projections:
/projections/{week}for the current NFL week of the current season. A week runs Tuesday to Monday; week 1 of 2026 began Tuesday, September 8.
Everything else needs Basic ($45/year, about $3.75 a month, or $5/month; 5,000 requests/month): any other week's projections, rest-of-season projections (/projections/ros), past seasons, /stats, /snaps, /deviations, /matchups, /schedule, /byes and all /news endpoints, including /news/latest. A free key calling those gets 403 with "reason": "basic_required" and an upgrade_url that opens Basic annual checkout for your account. Upgrade from your dashboard and the same key gets the answer. The betting data and the injury feed need Pro: /props, /defense, /usage, the injury changes feed, webhooks, /depth-charts and /inactives answer "reason": "pro_required" to Free and Basic keys, and below Pro /schedule games leave out odds and /projections/{week} rows leave out projected_stats (a top-level note says so).
HTTP/1.1 403 Forbidden
{
"error": "Player stats is not included in the free plan. The free plan covers player lookups and this week's projections (/projections/7). Basic unlocks every week, stats, snaps, deviations, matchups and news, with 5,000 requests a month: $45/year (about $3.75 a month) or $5/month. Your key stays the same when you upgrade. Basic annual: https://www.gridirondata.com/pages/dashboard.html?upgrade=basic&billing=annual (all plans: https://www.gridirondata.com/pages/pricing.html)",
"reason": "basic_required",
"required_plan": "basic",
"upgrade_url": "https://www.gridirondata.com/pages/dashboard.html?upgrade=basic&billing=annual",
"pricing_url": "https://www.gridirondata.com/pages/pricing.html",
"free_plan_includes": ["/players (profile, team, injury status)", "/projections/7 (season 2026)"]
}
What happens when I hit a limit?
The API answers 429 Too Many Requests. The reason field says which limit you hit: free_monthly_quota, quota_exceeded or rate_limited. For a free key out of its month, upgrade_url opens Basic annual checkout for the account; for a paid key over its quota it links to the pricing page. Back off and retry after the minute for rate_limited.
{
"error": "Free plan limit reached: you've used all 50 requests for this month (resets on the 1st, 00:00 UTC). Basic is $45/year (about $3.75 a month) or $5/month for 5,000 requests/month, and Pro is $29/month for 50,000 plus prop probabilities, betting lines and injury changes. Your key stays the same when you upgrade. Basic annual: https://www.gridirondata.com/pages/dashboard.html?upgrade=basic&billing=annual All plans: https://www.gridirondata.com/pages/pricing.html",
"reason": "free_monthly_quota",
"upgrade_url": "https://www.gridirondata.com/pages/dashboard.html?upgrade=basic&billing=annual"
}
Base URL
OpenAPI spec. Every endpoint below, with its parameters, response schemas, errors and the plan it needs (x-plan), is described in an OpenAPI 3.1 file you can import into Postman or Insomnia or feed to a client generator: Download OpenAPI spec (https://www.gridirondata.com/openapi.json).
Default season. Calls that omit ?season= use the current season, 2026. It stays 2026 through the off-season and moves to 2027 once 2027 data is loaded, so a season-less call never returns an empty spring. GET /health reports it as default_season.
Team codes. Every response uses one code per team: LAR (Rams), WAS (Washington) and JAX (Jacksonville). Older source rows spelled these LA, WSH and JAC; they are folded together, including matchup history. Filters and paths accept either spelling.
Injury status. injury_status is always one of ACTIVE, QUESTIONABLE, DOUBTFUL, OUT, IR, PUP, SUSPENDED, RESERVE or UNKNOWN. Our two sources spell these differently (Healthy/ACTIVE, IR/INJURY_RESERVE/IR-R); the stored spelling is in injury_status_raw. Players who can't play this week (OUT, IR, PUP, SUSPENDED, RESERVE, or team FA) have available: false.
Player ids. player_id is a readable name key (Patrick Mahomes II#QB). To join against a sportsbook or ESPN feed, use espn_player_id, an integer on players, projections and props, and look players up with /players?espn_id=3139477. Players also carry an ids map where we know them (gsis, pfr, pff, nfl, espn, sleeper), so /players?gsis_id=00-0034857 and ?pfr_id=AlleJo02 work too.
Data sources. Depth charts, official inactives and the ids crosswalk come from nflverse, licensed CC-BY-4.0, loaded once a day after nflverse's morning refresh. Injury statuses come from ESPN's injury report, cross-checked against Sleeper; stats, schedule and betting lines from ESPN.
Example Data
See the rich fantasy data you'll have access to. All examples show real data from our database.
Player Profile with Historical Data
Each player includes historical stats from 2020 through the current 2026 season and matchup history against every team. Weekly actuals and season totals carry all three scoring formats: the base fantasy_points / MISC_FPTS is standard, with _ppr / _half (weekly) and _PPR / _HALF (totals) variants alongside. Use ?scoring= on /stats to have the API resolve them for you (default ppr).
{
"player_id": "Jared Goff#QB",
"player_name": "Jared Goff",
"position": "QB",
"team": "DET",
"seasons": {
"2024": {
"weekly_stats": {
"1": {
"opponent": "LA",
"fantasy_points": 18.56, // standard
"fantasy_points_ppr": 18.56, // QBs rarely catch passes, so equal here
"fantasy_points_half": 18.56
},
"2": {
"opponent": "TB",
"fantasy_points": 29.08,
"fantasy_points_ppr": 29.08,
"fantasy_points_half": 29.08
}
// ... weeks 3-17
},
"season_totals": {
"MISC_FPTS": 372.5, // standard
"MISC_FPTS_PPR": 372.5,
"MISC_FPTS_HALF": 372.5,
"Rank": 5, "Rank_PPR": 6, "Rank_HALF": 6
// ... passing/rushing counting stats
}
},
"2023": { /* Full season data */ },
"2022": { /* Full season data */ },
"2021": { /* Full season data */ },
"2020": { /* Full season data */ }
},
"vs_opponent": {
"GB": {
"games_played": 8,
"avg_actual": 19.42,
"total_actual": 155.36,
"avg_projected": 18.9,
"avg_deviation": 0.52,
"by_season": { "2025": { "games_played": 2, "avg_actual": 21.3 } }
},
"CHI": {
"games_played": 8,
"avg_actual": 22.87,
"total_actual": 182.96,
"avg_projected": 19.4,
"avg_deviation": 3.47,
"by_season": { "2025": { "games_played": 2, "avg_actual": 25.1 } }
}
// ... all 32 teams
}
}
Matchup Analysis
Get historical performance data when a player faces a specific opponent. Perfect for start/sit decisions.
GET /matchups/Amon-Ra%20St.%20Brown%23WR/GB
{
"player_id": "Amon-Ra St. Brown#WR",
"player_name": "Amon-Ra St. Brown",
"position": "WR",
"team": "DET",
"opponent": "GB",
"stats": {
"games_played": 6,
"games_with_projections": 4,
"avg_actual": 21.45,
"total_actual": 128.7,
"avg_projected": 18.2,
"avg_deviation": 3.1,
"avg_deviation_percentage": 17.03,
"last_game_season": "2025",
"last_game_week": 13,
"by_season": {
"2025": { "games_played": 2, "avg_actual": 24.6 },
"2024": { "games_played": 2, "avg_actual": 19.9 }
}
}
}
// Use case: Amon-Ra averages 21.4 pts vs GB (6 games)
// but only 15.8 pts vs MIN (6 games) - start him vs GB!
Snap Share Trend
Catch a role change before the box score shows it. Compare a player’s recent snap share to his season average.
GET /snaps/Bucky%20Irving%23RB?season=2024&trend=4
{
"player_id": "Bucky Irving#RB",
"season": "2024",
"season_snap_pct_avg": 44.4,
"recent_snap_pct_avg": 57.0,
"trend_delta": 12.6
}
// Use case: +12.6 over the last 4 weeks means Irving is taking over
// the Tampa Bay backfield. Buy before the points catch up.
Projection Deviation Analysis
Identify boom/bust candidates by seeing how players perform vs their projections.
GET /deviations/Jahmyr%20Gibbs%23RB
{
"player_id": "Jahmyr Gibbs#RB",
"player_name": "Jahmyr Gibbs",
"position": "RB",
"season": "2024",
"avg_deviation": 3.2,
"weeks_analyzed": 16,
"weekly_deviations": [
{
"week": 1,
"actual_points": 22.4,
"projected_points": 14.5,
"deviation": 7.9,
"deviation_percentage": 54.5,
"opponent": "LA"
},
{
"week": 2,
"actual_points": 8.6,
"projected_points": 15.2,
"deviation": -6.6,
"deviation_percentage": -43.4,
"opponent": "TB"
}
]
}
// Use case: Moderate deviation = consistent player
// Gibbs ranges from 8.6 to 22.4 pts - reliable floor
Weekly Projections
Get fantasy point projections for all players, sorted by projected points.
GET /projections/12?position=TE
{
"week": "12",
"season": "2025",
"projections": [
{
"player_id": "Sam LaPorta#TE",
"player_name": "Sam LaPorta",
"position": "TE",
"team": "DET",
"week": "12",
"projected_points": 12.8,
"injury_status": "Healthy"
},
{
"player_id": "Travis Kelce#TE",
"player_name": "Travis Kelce",
"position": "TE",
"team": "KC",
"week": "12",
"projected_points": 11.4,
"injury_status": "Healthy"
},
{
"player_id": "George Kittle#TE",
"player_name": "George Kittle",
"position": "TE",
"team": "SF",
"week": "12",
"projected_points": 10.9,
"injury_status": "Questionable"
}
],
"count": 64
}
Historical data for backtesting (2020–2026)
Every week of every fantasy-relevant player from 2020 through the current 2026 season: weekly actuals in PPR, half and standard, snap share, matchup history, and actual-vs-projected deviations. It works all year, off-season included, which makes it a base for model builders: backtest a start/sit rule, measure how sticky production is from one season to the next, or check whether a player's usage predicted his breakout.
| What | Endpoint | Seasons |
|---|---|---|
| Weekly actuals + season totals (all three scorings, snap share joined per week) | /stats/{player_id}/{season} | 2020–2026 |
| Per-week snap share, trailing trend | /snaps/{player_id}?season= | 2020–2026 |
| History vs one opponent, split by season | /matchups/{player_id}/{opponent} | 2020–2026 |
| Weekly actual vs projected (boom/bust) | /deviations/{player_id}?season= | 2025–2026 |
| Everything for one player in one response | /players/{player_id} | 2020–2026 |
Example: how sticky is points per game from one season to the next?
Pull a player's weekly actuals for consecutive seasons and compare two forecasts of next season: last season as-is, and last season pulled 30% of the way back toward the position average. A dozen players over 2021–2025 is about 60 requests. Past seasons and weekly actuals are not on the free plan, so this runs on Basic ($5/month, 5,000 requests a month).
import os, statistics, requests
from urllib.parse import quote
API = "https://api.gridirondata.com/api/v1"
H = {"x-api-key": os.environ["GRIDIRON_API_KEY"]}
def ppg(player_id, season):
r = requests.get(f"{API}/stats/{quote(player_id, safe='')}/{season}", headers=H)
if r.status_code == 404:
return None # no data for that season
weeks = [w["points"] for w in r.json()["weekly_stats"].values()
if isinstance(w, dict) and w.get("points") is not None]
return sum(weeks) / len(weeks) if len(weeks) >= 8 else None
roster = ["Justin Jefferson#WR", "CeeDee Lamb#WR", "Davante Adams#WR", "Mike Evans#WR"]
seasons = range(2021, 2026)
table = {(p, s): ppg(p, s) for p in roster for s in seasons}
naive, shrunk = [], []
for s in seasons[:-1]:
this = {p: table[(p, s)] for p in roster if table[(p, s)] and table.get((p, s + 1))}
if not this:
continue
mean = statistics.mean(this.values())
for p, v in this.items():
nxt = table[(p, s + 1)]
naive.append(abs(v - nxt))
shrunk.append(abs(0.7 * v + 0.3 * mean - nxt))
print(f"naive MAE {statistics.mean(naive):.2f} shrunk MAE {statistics.mean(shrunk):.2f}")
On a 12-player roster across positions, 2021–2025, shrinking cut the average miss from 3.05 to 2.75 PPR points per game, most of it at RB and WR. The fuller script, with per-position results, the biggest year-over-year moves and a /deviations check of whether projection-beaters keep beating projections, is backtest.py in our examples.
To grade weekly projections against actuals across the whole league, see the Backtest Fantasy Projections Against Actuals guide.
Players
Offensive linemen (Pro): every center, guard and tackle (C, G, OT) on a team's roster or reserve list, with team, jersey number and injury status (no stats, snaps or projections). Lists leave them out unless you ask with position=C, G or OT or include_ol=true; a name search finds them. Below Pro, a search leaves them out and reports pro_only_matches, and asking for one directly answers 403 pro_required. Offensive line guide.
GET Get Player by ID
Retrieve detailed information for a specific player. On the free plan this returns the profile only; see the free plan.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| player_id required | string | Player ID in format: First Last#POSITION (e.g., Josh Allen#QB; defense: Houston Texans#DST). URL-encode the space and #. |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| season | string | Filter to specific season (e.g., "2026") |
Example Request
curl -H "x-api-key: YOUR_KEY" \ "https://api.gridirondata.com/api/v1/players/Josh%20Allen%23QB"
GET Search Players
Search for players by name, position, or team.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| name | string | Partial or full player name. Defenses also match by team code, nickname, or a DST suffix (TB, Bucs, Tampa Bay DST). |
| position | string | Position: QB, RB, WR, TE, K, DST; C, G, OT on Pro |
| team | string | Team abbreviation (e.g., BUF, KC). Matches the player's current-season team; JAC/JAX, WSH/WAS and LA/LAR are interchangeable. |
| include_ol | boolean | Pro: include offensive linemen when searching by team or position without a name |
| limit | integer | Max results (default: 50, max: 100) |
Example Request
curl -H "x-api-key: YOUR_KEY" \ "https://api.gridirondata.com/api/v1/players/search?name=Allen&position=QB"
GET Get Player by ESPN Id
Look a player up by ESPN athlete id, the numeric key most sportsbook and ESPN feeds carry. Returns the same record as Get Player by ID. Free plan included.
Example Request
curl -H "x-api-key: YOUR_KEY" \ "https://api.gridirondata.com/api/v1/players?espn_id=3139477"
GET Get Player by GSIS or PFR Id
Look a player up by NFL GSIS id (00-0034857), the key in nflverse and NFL data, or by Pro Football Reference id with ?pfr_id=AlleJo02. Returns the same record as Get Player by ID, including the ids map: gsis, pfr, pff, nfl, espn and sleeper, whichever we have. The crosswalk comes from nflverse's player file (CC-BY-4.0) and is refreshed daily; a player nflverse doesn't list has no ids and isn't found this way. Free plan included.
Example Request
curl -H "x-api-key: YOUR_KEY" \ "https://api.gridirondata.com/api/v1/players?gsis_id=00-0034857"
Example Response (abridged)
{
"player_id": "Josh Allen#QB",
"player_name": "Josh Allen",
"position": "QB",
"espn_player_id": 3918298,
"ids": {"gsis": "00-0034857", "pfr": "AlleJo02", "pff": "46601",
"nfl": "46076", "espn": "3918298", "sleeper": "4984"},
"seasons": {"...": "..."}
}
GET Get Player (Exact Match)
Get a specific player by exact name and position match.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| name required | string | Exact player name |
| position required | string | Position: QB, RB, WR, TE, K, DST; C, G, OT on Pro |
Example Request
curl -H "x-api-key: YOUR_KEY" \ "https://api.gridirondata.com/api/v1/players?name=Josh%20Allen&position=QB"
GET List Players
List all players with pagination.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| position | string | Filter by position |
| team | string | Filter by team, e.g. LAR (LA works too) |
| limit | integer | Results per page (default: 50, max: 100) |
| cursor | string | Pagination cursor from previous response |
Projections
GET Get Weekly Projections
Get fantasy point projections for all players for a specific week. Provider weekly projections are published as each week approaches, so for weeks that aren't out yet the API returns a prorated season-long projection (season points ÷ games) for every player. The projection_source field tells you which was used — weekly (real per-week number) or season_prorated (estimate) — and real weekly values replace the estimate automatically once published.
Players who can't play. For the current and upcoming weeks, players who are OUT, on IR, PUP, suspended or on a reserve list, and free agents, project 0 points with available: false, projection_source: "unavailable" and an unavailable_reason. projected_points_if_active keeps the number they'd have. Past weeks aren't changed by today's injury report.
Per-stat projections (Pro). Each row has projected_stats: pass_att, pass_cmp, pass_yds, pass_td, pass_int, rush_att, rush_yds, rush_td, rec, rec_yds and rec_td for the week. They're the season projection per game, scaled to the weekly points projection when there is one. Free and Basic keys get points only, and the response's top-level projected_stats is a note that the stat lines need Pro.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| week required | integer | NFL week number (1-18) |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| position | string | Filter by position |
| team | string | Filter by team, e.g. LAR (LA works too) |
| season | string | Season year (default: current NFL season) |
| source | string | Pass weekly to return ONLY real per-week projections (no season-prorated fallback) |
| scoring | string | Scoring format for projected_points: ppr (default), half, or standard. The echoed scoring field confirms which was applied. |
Example Request
curl -H "x-api-key: YOUR_KEY" \ "https://api.gridirondata.com/api/v1/projections/12?position=QB&scoring=half"
Example Response
{
"week": "12",
"season": "2026",
"scoring": "half",
"projections": [
{
"player_id": "Josh Allen#QB",
"player_name": "Josh Allen",
"position": "QB",
"team": "BUF",
"week": "12",
"projected_points": 24.5,
"projection_source": "season_prorated",
"espn_player_id": 3918298,
"on_bye": false,
"injury_status": "ACTIVE",
"injury_status_raw": "Healthy",
"available": true,
"projected_stats": {
"pass_att": 32.4, "pass_cmp": 21.2, "pass_yds": 250.0,
"pass_td": 1.76, "pass_int": 0.59,
"rush_att": 4.1, "rush_yds": 20.0, "rush_td": 0.18
}
},
{
"player_id": "Jalen Hurts#QB",
"player_name": "Jalen Hurts",
"position": "QB",
"team": "PHI",
"week": "12",
"projected_points": 0,
"projection_source": "unavailable",
"espn_player_id": 4040715,
"on_bye": false,
"injury_status": "IR",
"injury_status_raw": "IR",
"available": false,
"unavailable_reason": "IR",
"projected_points_if_active": 18.85,
"projected_stats": {"pass_yds": 0, "pass_td": 0, "...": 0}
}
],
"count": 32,
"prorated_count": 32,
"unavailable_count": 1
}
GET Get Rest-of-Season Projections
Projected fantasy points for every player over the regular-season weeks still to play (through week 18), for the current season. Basic and up; a free key gets 403 with "reason": "basic_required". The player's bye week is left out, and so is the current week once he has a stat line for it. games_remaining and remaining_weeks list what was counted.
How it's computed. Weekly projections are published a week or so ahead, so most remaining weeks don't have one yet. A week that has a published weekly projection counts that number (listed in weekly_projection_weeks). Every other week counts the player's per-game rate: his nearest published weekly projection (rate_source: weekly, rate_week), or season projection ÷ 17 when he has none (rate_source: season_prorated). There is no other modeling: no strength of schedule, no injury adjustment. method is per_game_rate when any week used the rate and weekly_sum when every remaining week had a weekly projection. When weekly projections carry stat fields, projected_stats totals them on the same basis.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| position | string | Filter by position |
| team | string | Filter by team, e.g. LAR (LA works too) |
| player_id | string | One player, e.g. Josh Allen#QB (URL-encode the # as %23) |
| season | string | Current season only (the default); any other season answers 400 |
| scoring | string | ppr (default), half, or standard |
Example Request
curl -H "x-api-key: YOUR_KEY" \ "https://api.gridirondata.com/api/v1/projections/ros?position=QB"
Example Response
{
"season": "2026",
"current_week": 8,
"position": "QB",
"scoring": "ppr",
"method": "per_game_rate",
"method_note": "per_game_rate: weeks without a published weekly projection count ...",
"projections": [
{
"player_id": "Josh Allen#QB",
"player_name": "Josh Allen",
"position": "QB",
"team": "BUF",
"bye_week": 7,
"games_remaining": 11,
"remaining_weeks": [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18],
"ros_points": 242.0,
"points_per_game": 22.0,
"rate_source": "weekly",
"rate_week": 8,
"weekly_projection_weeks": [8],
"method": "per_game_rate",
"injury_status": "Active"
}
],
"count": 38
}
Stats
GET Get Player Stats
Get historical stats for a player in a specific season: weekly fantasy points (2020 on) and season totals. From 2025 on, each week also carries box_score, that game's passing, rushing and receiving line from ESPN box scores (pass_yds, rush_att, rec, targets and the rest).
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| player_id required | string | Player ID |
| season | string | Season year (default: current NFL season) |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| scoring | string | Scoring format: ppr (default), half, or standard. Sets the resolved points on each week and the top-level season_points. The raw fantasy_points (standard) plus fantasy_points_ppr / fantasy_points_half variants are always present too. |
Alternative: You can also query by name and position:
Example Request
curl -H "x-api-key: YOUR_KEY" \ "https://api.gridirondata.com/api/v1/stats?name=Josh%20Allen&position=QB&season=2024&scoring=ppr"
Example Response
{
"player_id": "Justin Jefferson#WR",
"season": "2024",
"scoring": "ppr",
"season_points": 227.9,
"weekly_stats": {
"1": {
"opponent": "NYG",
"fantasy_points": 11.9, // standard (base)
"fantasy_points_ppr": 15.9,
"fantasy_points_half": 13.9,
"points": 15.9 // resolved for scoring=ppr
}
},
"season_totals": {
"MISC_FPTS": 149.9, // standard
"MISC_FPTS_PPR": 227.9,
"MISC_FPTS_HALF": 188.9,
"Rank": 8, "Rank_PPR": 3, "Rank_HALF": 5
}
}
Deviations
GET Get Projection Deviations
Analyze how a player performs vs their projections. Useful for identifying boom/bust candidates.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| player_id required | string | Player ID |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| season | string | Season year (default: current NFL season) |
Alternative: Query by name and position:
Example Response
{
"player_id": "Josh Allen#QB",
"player_name": "Josh Allen",
"position": "QB",
"season": "2025",
"avg_deviation": 3.2,
"weeks_analyzed": 11,
"weekly_deviations": [
{
"week": 1,
"actual_points": 28.4,
"projected_points": 24.5,
"deviation": 3.9,
"deviation_percentage": 15.9,
"opponent": "ARI"
}
]
}
Snap Counts
GET Get Snap Share
Per-week snap share — the percentage of his team’s offensive plays a player was on the field for — plus special-teams snaps, a season average, and a trailing trend. Snap share moves before box-score production does, so it is the earliest reliable signal of a role change.
Data is sourced from nflverse / Pro-Football-Reference game-level snap counts. Percentages are
returned as 0–100. Seasons 2020–2026 are available
(the current season updates weekly); regular season (weeks 1–18) only.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| player_id required | string | Player ID (First Last#POS, URL-encoded) |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| season | string | Season year (default: current NFL season) |
| trend | integer | Weeks in the trailing average, 1–18 (default: 4) |
Alternative: Query by name and position:
Response Fields
| Field | Type | Description |
|---|---|---|
| season_snap_pct_avg | number | Average snap share across every week he took a snap |
| recent_snap_pct_avg | number | Average over the most recent trend weeks played |
| trend_delta | number | Recent minus season. Positive = trending up (role growing) |
| games_with_snaps | integer | Weeks with a non-zero snap share. Zero-snap weeks are excluded from averages |
| weekly_snaps[] | array | Per week: snap_pct, offense_snaps, st_snaps, st_pct, team, opponent |
Example Response
GET /snaps/Bucky%20Irving%23RB?season=2024&trend=4
{
"player_id": "Bucky Irving#RB",
"player_name": "Bucky Irving",
"position": "RB",
"season": "2024",
"games": 17,
"games_with_snaps": 17,
"season_snap_pct_avg": 44.4,
"recent_snap_pct_avg": 57.0,
"trend_weeks": 4,
"trend_delta": 12.6,
"totals": {
"games": 17,
"offense_snaps": 516,
"offense_pct_avg": 44.4,
"st_snaps": 41,
"st_pct_avg": 8.6
},
"weekly_snaps": [
{ "week": 1, "offense_snaps": 20, "snap_pct": 31.0, "st_snaps": 8, "st_pct": 30.0, "team": "TB", "opponent": "WAS" },
{ "week": 18, "offense_snaps": 48, "snap_pct": 72.0, "st_snaps": 0, "st_pct": 0.0, "team": "TB", "opponent": "NO" }
],
"source": "nflverse / Pro-Football-Reference game-level snap counts"
}
// Irving opened 2024 at 31% of Tampa Bay's snaps and finished at 72%.
// trend_delta of +12.6 flags the takeover while it is still happening.
Snap share on /stats
Snap data is also joined onto the stats endpoint, so you can read production and playing time
together without a second request. Each weekly entry gains snap_pct and
offense_snaps, and the response carries a snap_counts block.
GET /stats/Bucky%20Irving%23RB/2024
{
"weekly_stats": {
"1": { "points": 12.6, "opponent": "WAS", "snap_pct": 31.0, "offense_snaps": 20 }
},
"snap_counts": {
"weekly": { ... },
"totals": { "games": 17, "offense_snaps": 516, "offense_pct_avg": 44.4 }
}
}
Note: weeks played and weeks with snaps are not always the same set — a blocking tight end can log snaps with no fantasy line. Weeks missing from one side simply omit the corresponding fields rather than reporting zero.
News
GET League-wide News Feed
Recent news across every tracked player, newest first, in one call: signings, trades, releases, injuries and depth-chart moves. Use it through free agency (March) and the NFL Draft (April) instead of polling player by player. The scrapers run year-round.
Plans: Basic and above get the full feed: 90 days of history, filters and paging. Free keys get 403 with "reason": "basic_required" and an upgrade_url; see the free plan.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| since | string | ISO-8601 (2027-03-09T00:00:00Z) or unix seconds. Default: days ago. Maximum lookback: 90 days. |
| days | integer | Lookback when since is omitted (default 7, max 90) |
| impact | string | injury, transaction, role or general; comma-separate several (transaction,role) |
| position | string | QB, RB, WR, TE; comma-separate several |
| team | string | Player's current team, e.g. LAR (LA works too) |
| limit | integer | Items per page (default 25, max 100) |
| cursor | string | The next_cursor from the previous page. Absent on the last page. |
Example Request
curl -H "x-api-key: YOUR_KEY" \ "https://api.gridirondata.com/api/v1/news/latest?since=2027-03-09T00:00:00Z&impact=transaction&position=WR"
Example Response
{
"since": "2027-03-09T00:00:00Z",
"until": "2027-03-11T14:02:10Z",
"count": 1,
"filters": {"impact": ["transaction"], "position": ["WR"]},
"news": [
{
"player_id": "Davante Adams#WR",
"player_name": "Davante Adams",
"position": "WR",
"team": "LAR",
"headline": "Davante Adams expected at start of Rams offseason program",
"summary": "",
"impact": "transaction",
"source": "pft-nbc",
"url": "https://profootballtalk.nbcsports.com/...",
"published_at": "2027-03-10T19:41:00Z"
}
],
"next_cursor": "WyIyMDI3LTAzLTEwVDE5OjQxOjAwWiIs..."
}
One headline about two players appears once per player, so a position or team filter still catches it.
GET Get Player News
Recent NFL news headlines for a player, newest first. Each item is tagged with an impact category (injury, transaction, role, or general) to help surface start/sit-relevant updates. Items are refreshed throughout the day, all year, and roll off after 90 days. For every player at once, use /news/latest.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| player_id required | string | Player ID (e.g. Malik Nabers#WR) |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| days | integer | Lookback window in days (default: 14, max: 90) |
| limit | integer | Max headlines to return (default: 10, max: 50) |
| impact | string | Filter by category: injury, transaction, role, or general |
Alternative: Query by name and position:
Example Response
{
"player_id": "Malik Nabers#WR",
"days": 14,
"count": 2,
"news": [
{
"headline": "Nabers (knee) full participant at Wednesday practice",
"summary": "Giants WR Malik Nabers practiced fully and is trending toward playing Sunday.",
"impact": "injury",
"source": "espn-nfl",
"url": "https://www.espn.com/nfl/story/_/id/00000000",
"published_at": "2026-09-10T17:22:00Z"
},
{
"headline": "Nabers feeling 'good' but mum on playing Week 1",
"summary": "",
"impact": "general",
"source": "espn-nfl",
"url": "https://www.espn.com/nfl/story/_/id/00000001",
"published_at": "2026-09-01T03:21:40Z"
}
]
}
No news yet? When a player has no recent news, the response includes a request_scrape link you can call to queue an on-demand scrape (see next endpoint):
{
"player_id": "Max Bredeson#RB",
"days": 14,
"count": 0,
"news": [],
"message": "No news found for Max Bredeson#RB in the last 14 days.",
"request_scrape": {
"method": "POST",
"href": "https://api.gridirondata.com/api/v1/news/Max%20Bredeson%23RB/request",
"description": "No news yet — POST to this link to request a fresh scrape for this player."
}
}
POST Request a News Scrape
Queue an on-demand news scrape for a specific player — useful when GET /news/{player_id} returns no results and you want the latest sources checked immediately. The request is accepted and processed asynchronously; poll GET /news/{player_id} shortly after to see any new headlines. No request body is required.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| player_id required | string | Player ID (e.g. Max Bredeson#RB) |
Example Response (202 Accepted)
{
"player_id": "Max Bredeson#RB",
"status": "accepted",
"message": "Scrape requested. New headlines (if any are found in current sources) will appear shortly.",
"poll": {
"method": "GET",
"href": "https://api.gridirondata.com/api/v1/news/Max%20Bredeson%23RB"
}
}
Injury Changes (Pro)
GET Get Injury Status Changes
Returns every player whose injury status changed since since. This is a Pro plan feature; Free and Basic keys get 403 with "reason": "pro_required", an upgrade_url and a pro_includes list of what the plan adds.
GridIron checks ESPN's league-wide injury report every 2 minutes from 8:00 am to 1:00 am ET (every 10 minutes overnight), so a status change usually appears here within a few minutes of ESPN posting it, with a detected_at time. A full refresh still runs daily at 11:00 ET and after inactives on game days, and once a day around 12:30 pm ET Sleeper's player data is checked for anyone ruled out whom ESPN hasn't caught yet. Poll with the next_since value from your last response to get only new changes. Statuses use the standard values; a change that is only a different spelling (Healthy to ACTIVE) isn't reported.
Freshness. Each change says where it came from and which game it applies to:
source:espn(ESPN's injury feed, polled every 2 minutes),espn-roster(ESPN team rosters, checked daily; offensive linemen),sleeper(the daily Sleeper cross-check) orstats-refresh(the scheduled refresh). Older changes from the scheduled refresh havenull.game_idandgame_kickoff_utc: the player's team's next game that hadn't kicked off atdetected_at(the samegame_idas/schedule), ornullif the team has no game left.team_freshness(top level, one entry per team, or just theteamyou filter on):last_checked_atis the last good poll of ESPN's injury feed that listed the team, andespn_updated_atis the newest update ESPN made to any of that team's entries.last_checked_atat the top level tells a quiet feed from a stale one.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| since | string | ISO-8601 (2026-09-27T16:00:00Z) or unix seconds. Default: 24 hours ago. Maximum lookback: 14 days. Exclusive. |
| position | string | Filter by position, e.g. WR |
| team | string | Filter by team, e.g. BUF |
| limit | integer | Maximum changes returned (default 500, max 1000). A refresh run is never split across responses. |
| season | string | Season year (default: current NFL season) |
Example Request
curl -H "x-api-key: YOUR_PRO_KEY" \ "https://api.gridirondata.com/api/v1/injuries/changes?since=2026-09-27T16:00:00Z"
Example Response
{
"season": "2026",
"since": "2026-09-27T16:00:00Z",
"until": "2026-09-27T17:05:41Z",
"count": 1,
"changes": [
{
"player_id": "Josh Allen#QB",
"player_name": "Josh Allen",
"position": "QB",
"team": "BUF",
"previous_status": "QUESTIONABLE",
"status": "OUT",
"previous_status_raw": "Questionable",
"status_raw": "Out",
"detected_at": "2026-09-27T16:55:12Z",
"source": "espn",
"game_id": "401772938",
"game_kickoff_utc": "2026-09-27T17:00:00Z"
}
],
"next_since": "2026-09-27T16:55:12Z",
"last_checked_at": "2026-09-27T17:04:02Z",
"team_freshness": {
"BUF": {"last_checked_at": "2026-09-27T17:04:02Z", "espn_updated_at": "2026-09-27T16:54Z"}
},
"truncated": false
}
Depth Charts and Inactives (Pro)
Both are Pro plan features; Free and Basic keys get 403 with "reason": "pro_required". The data comes from nflverse (CC-BY-4.0), which refreshes around 07:00 UTC; GridIron loads it at 08:00 UTC every day and again on Sunday mornings. If a load fails, the previous day's data stays and as_of / loaded_at say how old it is.
GET Get Depth Charts
Every team's depth chart as of this morning's nflverse refresh (as_of is the snapshot time). Each entry has the player, our player_id when the player is in /players (most defenders aren't), espn_id, gsis_id, the depth chart position, formation, slot and rank (1 = starter). The starting QB for every team is one call: ?position=QB&starters=true.
nflverse builds these from public depth charts (ESPN-sourced since 2025). They are not official team depth charts, and a change late in the week shows up the morning after nflverse picks it up. For who is actually out, use /injuries/changes.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| team | string | One team, e.g. BUF (default: all 32) |
| position | string | Depth chart position: QB, RB, WR, TE, LT, C, PK, LDE, ... K, OT, G and OL also work. |
| starters | boolean | true: rank 1 only (same as rank=1). Three-receiver sets have three rank-1 WRs, one per slot. |
| rank | integer | Only this rank, e.g. 2 for backups |
| season | string | Season year (default: current season) |
Example Request
curl -H "x-api-key: YOUR_PRO_KEY" \ "https://api.gridirondata.com/api/v1/depth-charts?position=QB&starters=true"
Example Response (abridged)
{
"season": "2026",
"as_of": "2026-11-01T07:02:30Z",
"loaded_at": "2026-11-01T08:00:41Z",
"count": 32,
"entries": [
{
"team": "BUF",
"player_name": "Josh Allen",
"player_id": "Josh Allen#QB",
"espn_id": "3918298",
"gsis_id": "00-0034857",
"position": "QB",
"position_name": "Quarterback",
"formation": "3WR 1TE",
"slot": 9,
"rank": 1,
"as_of": "2026-11-01T07:02:30Z"
}
],
"note": "Depth charts as of the daily nflverse refresh (as_of). ...",
"source": "Depth charts, inactives and player ids: nflverse (github.com/nflverse/nflverse-data), CC-BY-4.0."
}
GET Get Official Inactives
The NFL's official game-day inactive list by week and team, with gsis, ESPN, Sleeper and PFR ids. It is published after the games, usually several days later (nflverse's weekly roster file), so it is for audit and backtesting: checking what a model assumed against who actually sat. It is not a pre-kickoff feed; for game-day decisions use /injuries/changes or webhooks.
A week fills in as teams are published. teams_published is how many teams have inactives listed so far, and teams_played how many teams' games that week are final; the week is complete when they match.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| season | string | Season year (default: current season) |
| week | integer | 1–22 (19 and up are playoffs). Default: the newest week with any inactives published. |
| team | string | One team, e.g. CIN |
Example Request
curl -H "x-api-key: YOUR_PRO_KEY" \ "https://api.gridirondata.com/api/v1/inactives?week=2&team=CIN"
Example Response (abridged)
{
"season": "2026",
"week": 2,
"team": "CIN",
"count": 7,
"inactives": [
{
"season": "2026", "week": 2, "game_type": "REG", "team": "CIN",
"player_name": "Josh Johnson", "player_id": "Josh Johnson#QB",
"position": "QB", "jersey_number": "11",
"gsis_id": "00-0026300", "espn_id": "11394", "sleeper_id": "260", "pfr_id": "JohnJo05"
}
],
"teams_published": 1,
"teams_played": 1,
"weeks_available": [1, 2, 3, 4, 5, 6, 7, 8],
"loaded_at": "2026-11-01T08:00:41Z",
"note": "Official NFL game-day inactives, published after the games (usually days later) ...",
"source": "Depth charts, inactives and player ids: nflverse (github.com/nflverse/nflverse-data), CC-BY-4.0."
}
Props and Defense vs Position (Pro)
Tools for modeling player props. Both are Pro plan features; Free and Basic keys get 403 with "reason": "pro_required". Stat lines come from ESPN box scores for every game since 2025, loaded after each week's games. Walkthrough with a runnable script: Price NFL player props with a probability API.
GET Get Prop Probability
The probability a player goes over or under a stat line, with fair (no-vig) American odds to compare against the book's price. How it works:
- Mean: the player's season projection per game, shrunk toward his own average this season. The projection counts as 6 games, so by mid-season his own games carry about half the weight.
- Spread: variance proportional to the mean, fitted per position and stat on every 2025–26 game by players with a real role. Receptions, receiving yards and rushing yards use a right-skewed (gamma) distribution, since a few big games pull their average above a typical game; passing stats and rush attempts are normal; touchdowns and interceptions are Poisson.
- Track record: every response includes the stat's walk-forward backtest (each week predicted from earlier weeks only, at lines from 0.6× to 1.4× the player's mean): its Brier score and log loss, the Brier score of always guessing the base rate, and a calibration table.
Players on bye or unavailable (OUT, IR, ...) get p_over 0. The response also has the week's game, its betting line and the team's implied points. This is a model estimate, not betting advice.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| player_id | string | e.g. Drake Maye#QB. Or pass espn_id, or name and position. |
| stat required | string | pass_yds, pass_td, pass_att, pass_cmp, pass_int, rush_yds, rush_att, rush_td, rec, rec_yds or rec_td |
| line required | number | The line, e.g. 245.5, or up to 10 separated by commas (225.5,245.5,265.5). Whole-number lines also return p_push. |
| week | integer | NFL week, 1–18 (default: the current week, or the next one if his game this week is already final) |
| season | string | Season year (default: current season) |
Example Request
curl -H "x-api-key: YOUR_PRO_KEY" \ "https://api.gridirondata.com/api/v1/props?player_id=Drake%20Maye%23QB&stat=pass_yds&line=245.5"
Example Response (abridged)
{
"player_id": "Drake Maye#QB",
"espn_player_id": 4431452,
"team": "NE",
"week": 8,
"stat": "pass_yds",
"injury_status": "ACTIVE",
"available": true,
"game": {"opponent": "NYJ", "home": true, "implied_team_points": 26.5, "odds": {"...": "..."}},
"projection": {
"mean": 249.3,
"sd": 58.1,
"distribution": "normal",
"season_projection_per_game": 239.03,
"games_played": 7,
"season_average": 262.4
},
"lines": [
{"line": 245.5, "p_over": 0.526, "p_under": 0.474, "p_push": 0,
"fair_odds_over": -111, "fair_odds_under": 111}
],
"model": {"k": 26.5, "backtest": {"n": 2135, "brier": 0.172, "brier_base_rate": 0.25, "calibration": ["..."]}},
"disclaimer": "Model estimate, not betting advice. ..."
}
GET Get Defense vs Position
What each defense allows per game to QBs, RBs, WRs and TEs: fantasy points plus the stats behind them (passing and rushing yards and TDs, receptions, receiving yards, targets). rank 1 allows the most fantasy points per game, the best matchup for that position.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| position | string | QB, RB, WR or TE. Without it, all four come back under by_position. |
| last | integer | Only the last N weeks with games (default: the whole season) |
| team | string | One defense, e.g. MIA (its rank is still among all 32) |
| scoring | string | ppr (default), half or standard |
| season | string | Season year (default: current season) |
Example Request
curl -H "x-api-key: YOUR_PRO_KEY" \ "https://api.gridirondata.com/api/v1/defense?position=WR&last=4"
Example Response (abridged)
{
"season": "2026",
"scoring": "ppr",
"weeks": [4, 5, 6, 7],
"position": "WR",
"defenses": [
{
"team": "MIA",
"rank": 1,
"games": 4,
"fantasy_points_allowed_per_game": 46.2,
"per_game": {"targets": 24.5, "rec": 16.8, "rec_yds": 221.3, "rec_td": 1.5}
}
],
"rank_meaning": "1 = allows the most fantasy points per game to this position",
"source": "ESPN box scores"
}
GET Get Target Share and Carry Share
A player's weekly usage: his targets against his team's targets (target_share_pct) and his rush attempts against his team's rush attempts (carry_share_pct), with receptions, receiving yards and snap share, plus season totals, a trailing window and the trend between them. Team totals count every player with a line in the box score, and a week he played without a target or carry counts as 0. From ESPN box scores, 2025 onward. Shares are 0–100.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| player_id | string | Player ID (or pass espn_id, or name and position) |
| season | string | Season year, 2025 or later (default: current season) |
| trend | integer | Games in the trailing window (default 4) |
Example Request
curl -H "x-api-key: YOUR_PRO_KEY" \ "https://api.gridirondata.com/api/v1/usage?player_id=CeeDee%20Lamb%23WR&trend=4"
Example Response (abridged)
{
"player_id": "CeeDee Lamb#WR",
"season": "2026",
"season_totals": {"games": 7, "targets": 63, "team_targets": 245,
"target_share_pct": 25.7, "carry_share_pct": 1.1},
"recent": {"games": 4, "targets": 40, "team_targets": 140,
"target_share_pct": 28.6, "trend_weeks": 4},
"trend_delta": {"target_share_pct": 2.9, "carry_share_pct": 0.0},
"weekly_usage": [
{"week": 7, "team": "DAL", "opponent": "WAS", "targets": 11, "team_targets": 36,
"target_share_pct": 30.6, "rec": 8, "rec_yds": 104, "rush_att": 0,
"team_rush_att": 24, "carry_share_pct": 0.0, "snap_pct": 91.2}
],
"source": "ESPN box scores; snap share from nflverse / Pro-Football-Reference"
}
Injury Change Webhooks (Pro)
Instead of polling /injuries/changes, a Pro key can register one HTTPS URL. Within a few minutes of a status change (the same checks that feed /injuries/changes), GridIron POSTs it to that URL as JSON. Set it up under Injury Change Webhook on your dashboard, or with the endpoints below. Free and Basic keys get 403 with "reason": "pro_required". These calls never count against your monthly requests.
POST Register or update the webhook
Body: {"url": "https://example.com/gridiron"}. The URL must use HTTPS on port 443 and resolve to public IP addresses; private, loopback, link-local and cloud metadata addresses are refused with 400 "reason": "invalid_url". The first registration answers 201 with a signing_secret (whsec_…). It is shown only once, so store it. Updating the URL keeps the secret and re-enables a disabled webhook.
GET /webhooks returns the webhook (URL, status, last delivery, a secret_hint), never the secret. DELETE /webhooks removes it. POST /webhooks/secret replaces the signing secret and shows the new one once; the old one stops working at once. POST /webhooks/test sends a "type": "test" event right away and returns delivered, status_code and duration_ms (one every 10 seconds).
Example Request
curl -X POST -H "x-api-key: YOUR_PRO_KEY" -H "Content-Type: application/json" \
-d '{"url": "https://example.com/gridiron"}' \
"https://api.gridirondata.com/api/v1/webhooks"
What we send
A POST with Content-Type: application/json and these headers: X-GridIron-Event (injury.changes or test), X-GridIron-Event-Id, X-GridIron-Timestamp (unix seconds) and X-GridIron-Signature. The changes have the polling feed's core fields, including source (null on older changes); previous_status and status carry the source spelling (the polling feed's *_raw values). A run with more than 250 changes is split into numbered parts. Event ids are stable, so use them to drop a duplicate.
{
"id": "evt_3f1c2a9b8d7e6f5a4b3c2d1e",
"type": "injury.changes",
"created_at": "2026-10-25T17:00:09Z",
"season": "2026",
"detected_at": "2026-10-25T17:00:05Z",
"count": 1,
"part": 1,
"parts": 1,
"changes": [
{
"player_id": "Josh Allen#QB",
"player_name": "Josh Allen",
"position": "QB",
"team": "BUF",
"previous_status": "Questionable",
"status": "Out",
"detected_at": "2026-10-25T17:00:05Z",
"source": "espn"
}
]
}
Delivery rules. Answer with any 2xx within 5 seconds. Timeouts, connection errors, 408, 429 and 5xx are retried twice, after 1 and 4 seconds; other 4xx answers aren't retried. Redirects aren't followed. After 5 failed deliveries in a row the webhook is disabled; fix the endpoint, then save the URL again on your dashboard to turn it back on. Pushes stop while a plan is paused or no longer Pro.
Verify the signature
X-GridIron-Signature is sha256= followed by the hex HMAC-SHA256 of <X-GridIron-Timestamp>.<raw request body>, keyed with your signing secret. Compute it over the raw bytes before parsing the JSON, compare in constant time, and reject old timestamps to block replays.
import hashlib, hmac, time
def verify(secret, headers, raw_body):
ts = headers["X-GridIron-Timestamp"]
if abs(time.time() - int(ts)) > 300:
return False
expected = hmac.new(secret.encode(), f"{ts}.".encode() + raw_body,
hashlib.sha256).hexdigest()
return hmac.compare_digest("sha256=" + expected, headers["X-GridIron-Signature"])
Matchups
GET Get vs Opponent History
Get historical performance data for a player against a specific opponent.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
| player_id required | string | Player ID |
| opponent required | string | Opponent team abbreviation (e.g., KC, NE) |
Alternative: Query by name and position:
Example Request
curl -H "x-api-key: YOUR_KEY" \ "https://api.gridirondata.com/api/v1/matchups/Josh%20Allen%23QB/KC"
Schedule and Bye Weeks
The NFL regular-season schedule from the ESPN scoreboard: every game with its week, home and away teams, kickoff time in UTC, and the final score once it's played. Bye weeks are worked out from the same schedule. Team codes match the rest of the API (WAS, JAX, LAR). Basic and above; a free key gets 403 with "reason": "basic_required".
GET Get the Schedule
Games sorted by week, then kickoff. status is scheduled, in_progress or final; home_score and away_score are null until the game is final. Scores are updated after each week's games. With team, the response adds that team's bye_week.
Betting lines (Pro). On Pro keys, each game has odds once a line is posted (DraftKings, via the ESPN scoreboard): spread_home (negative when the home team is favored), over_under, both moneylines and each team's implied points, (over_under − spread_home) / 2 for the home team. Lines are refreshed daily until kickoff and kept afterwards, so a final game shows the last line before kickoff. odds is null when no line was captured. Basic keys get the schedule without odds, and a top-level odds note says lines need Pro.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| season | string | Season year (default: current season) |
| week | integer | One week, 1–18 |
| team | string | Only this team's games (e.g., KC, WAS) |
Example Request
curl -H "x-api-key: YOUR_KEY" \ "https://api.gridirondata.com/api/v1/schedule?season=2026&week=8&team=GB"
Example Response
{
"season": "2026",
"count": 1,
"games": [
{
"week": 8,
"game_id": "401873023",
"kickoff_utc": "2026-10-30T00:15:00Z",
"home": "GB",
"away": "CAR",
"status": "scheduled",
"home_score": null,
"away_score": null,
"odds": {
"provider": "DraftKings",
"spread_home": -3.0,
"over_under": 47.5,
"home_moneyline": -155,
"away_moneyline": 130,
"implied_home_points": 25.25,
"implied_away_points": 22.25,
"captured_at": "2026-10-23T14:00:05Z"
}
}
],
"updated_at": "2026-10-23T14:00:05Z",
"source": "ESPN scoreboard",
"week": 8,
"team": "GB",
"bye_week": 5
}
GET Get Bye Weeks
Each team's bye week, plus the teams on bye in each week. season defaults to the current season.
Example Request
curl -H "x-api-key: YOUR_KEY" \ "https://api.gridirondata.com/api/v1/byes?season=2026"
Example Response
{
"season": "2026",
"byes": {"ARI": 14, "ATL": 11, "BAL": 13, "BUF": 7, ...},
"by_week": {"5": ["CAR", "KC"], "6": ["CIN", "DET", "MIA", "MIN"], ...},
"source": "ESPN scoreboard"
}
Health Check
GET Health Check
Check API status and version.
Example Response
{
"status": "healthy",
"version": "1.0.0",
"default_season": "2026"
}
Error Codes
| Code | Description |
|---|---|
| 400 | Bad Request - Invalid parameters |
| 401 | Unauthorized - Invalid or missing API key |
| 403 | Forbidden - The endpoint needs a higher plan ("reason": "basic_required" for a free key outside the free plan, or "reason": "pro_required", each with an upgrade_url), or the key's subscription isn't active: subscription_canceled (with a signup_url; sign up again with the same email to get the same key back), payment_failed or subscription_inactive (with a billing_url to update your card), or paused ("reason": "subscription_paused", with resumes_at, resume_url and resume_steps). A paused plan turns back on by itself on August 1; to resume sooner, click "Resume now" under Billing & Subscription on your dashboard. |
| 404 | Not Found - Resource doesn't exist |
| 409 | Conflict - Multiple matches found (use player_id for exact match) |
| 429 | Too Many Requests - A rate limit or quota was hit. See reason and upgrade_url in the body. |
| 500 | Internal Server Error |
Frequently asked questions
How do I authenticate with the GridIron Data API?
Send your key in the x-api-key header on every request to https://api.gridirondata.com/api/v1. A free key allows 50 requests a month with no credit card.
What are the rate limits?
Free: 50 requests/month at 10 per minute, for player lookups and the current week's projections only. Basic: 5,000 requests/month at 30 per minute. Pro: 50,000 requests/month at 100 per minute. Enterprise: custom.
What happens when I hit a limit?
The API returns HTTP 429 with a JSON body. The reason field says which limit you hit (free_monthly_quota, quota_exceeded or rate_limited). For a free key upgrade_url opens Basic annual checkout; for a paid key it links to the pricing page.
How do I get injury changes on game day?
Pro keys can call GET /api/v1/injuries/changes with ?since= set to an ISO-8601 or unix timestamp. It returns each player whose injury status changed since that time, with the old and new status. Changes appear within a few minutes of ESPN's injury report.
Which scoring formats are supported?
PPR is the default. Add ?scoring=half or ?scoring=standard to /projections and /stats.
What can I do with the API in the off-season?
Historical data from 2020 through 2026 (weekly actuals, snap share, matchup history and deviations) works all year for backtesting and dynasty research; see Historical data. News keeps flowing through free agency and the draft: GET /api/v1/news/latest returns it league-wide on Basic and above. Season-less calls keep answering with the 2026 season until 2027 data is loaded.
How do I get NFL injury data in Python?
Each player in GET /api/v1/projections/{week} and GET /api/v1/players/{player_id} carries an injury_status field. Send your key in the x-api-key header with any HTTP client, such as Python’s requests. For injury news, call GET /api/v1/news/{player_id}?impact=injury. Pro keys can poll GET /api/v1/injuries/changes?since= for status changes on game day.
import requests
r = requests.get(
"https://api.gridirondata.com/api/v1/projections/12",
params={"position": "TE"},
headers={"x-api-key": "YOUR_API_KEY"},
)
for p in r.json()["projections"]:
print(p["player_name"], p["team"], p["injury_status"])
For game-day alerts, see Track NFL injury changes on game day.
Is there a free NFL stats API?
GridIron Data has a free plan: 50 requests a month, 10 a minute, and no credit card. It covers player lookups (name, position, team, injury status and jersey number) and projections for the current NFL week. Weekly stats and 2020–2026 actuals, other weeks, deviations, snap share, matchups and news need Basic ($5/month), and the betting data (/props, /defense, odds, per-stat projections) and injury changes feed need Pro ($29/month). Sign up, confirm your email, and copy the key from your dashboard.
How do I get player projections via API?
Call GET /api/v1/projections/{week} with your key in the x-api-key header. Filter with ?position= and ?team=, pick ?season= (default: the current season) and add ?scoring=half or ?scoring=standard (PPR is the default). For weeks whose weekly projections are not published yet, the API returns a prorated season-long projection and marks it projection_source: season_prorated. GET /api/v1/players/{player_id} returns a player’s season projections.
Does the API have player prop and betting data?
Yes, on the Pro plan ($29/month or $249/year). GET /api/v1/props?player_id=&stat=&line= returns P(over), P(under) and fair no-vig American odds for a QB, RB, WR or TE stat line, with the projected mean, recent games and the model's walk-forward backtest. GET /api/v1/defense ranks what each defense allows by position. Pro keys also get odds (spread, total, moneylines, implied team points) on /schedule games and per-stat projections on /projections/{week}. Free and Basic keys get 403 pro_required. Prop probabilities are a model estimate, not betting advice. See Price NFL player props.
How often is the data updated?
Weekly stats for the current season land the day after games, at about 11:00 ET. Injury status is checked against ESPN's injury report every 2 minutes from 8:00 am to 1:00 am ET (every 10 minutes overnight), with a full refresh daily at 11:00 ET and after inactives on game days. Betting lines on /schedule refresh daily until kickoff, and the box scores behind /props and /defense load after each week's games. Player news is refreshed throughout the day, all year. Snap counts update weekly during the season. Projections and stats refresh once a day, so cache responses.