Skip to main content
Version: 0.0.70

🏈 The NFL with sportsdataverse-py

Welcome to gridiron data! πŸŽ‰ In a handful of lines you're about to pull standings, rosters, weekly injury reports, NextGen Stats tracking leaderboards, and full play-by-play β€” straight from the source.

sportsdataverse.nfl leads with the premium api.nfl.com native endpoints (nfl_standings, nfl_rosters, nfl_injuries, …) and the NextGen Stats tracking API (nfl_ngs_*), backed by the battle-tested nflverse release loaders (load_nfl_pbp, load_nfl_player_stats, …). ESPN (espn_nfl_*) rides shotgun as a quick, no-auth secondary path.

Every accessor hands you a tidy polars DataFrame by default β€” pass return_as_pandas=True for pandas. If you've used the R packages nflfastR / nflreadr, or the Python nflreadpy, you're already home: the load_* names line up. Let's hike it! 🏈

🧰 The toolbox​

Three data families, one module. The 🟒 premium rows lead with native api.nfl.com / NextGen Stats endpoints; the πŸ“¦ rows read versioned nflverse release parquets; ESPN is the πŸ”΅ quick secondary path. Click any name for the full reference.

FunctionWhat it gives youSource
nfl_standingsTeam standings for a season/week β€” one row per team🟒 premium (NFL.com)
nfl_rostersSeason rosters, one row per team (players nested)🟒 premium (NFL.com)
nfl_injuriesWeekly injury report, one row per player🟒 premium (NFL.com)
nfl_weeksThe week calendar (bye weeks, date ranges)🟒 premium (NFL.com)
nfl_weekly_game_detailsRich per-game details for a week (drive charts, standings)🟒 premium (NFL.com)
nfl_game_summariesLive game state, one row per game🟒 premium (NFL.com)
nfl_teamSingle-team detail by team_id🟒 premium (NFL.com)
nfl_ngs_statboardNextGen Stats season leaderboard (passing/rushing/receiving)🟒 premium (NextGen Stats)
nfl_ngs_leadersNextGen top-N highlight boards (speed, YAC over expected, …)🟒 premium (NextGen Stats)
nfl_ngs_league_scheduleNextGen schedule β€” source of NGS gameIds🟒 premium (NextGen Stats)
nfl_ngs_gamecenter_overviewPer-game NextGen player splits (passers/rushers/…)🟒 premium (NextGen Stats)
load_nfl_pbpFull nflfastR play-by-play (370+ columns)πŸ“¦ nflverse release
load_nfl_player_statsWeekly player box-score statsπŸ“¦ nflverse release
load_nfl_nextgen_statsNextGen Stats back to 2016 (release parquet)πŸ“¦ nflverse release
load_nfl_rostersSeason rosters with IDs & biosπŸ“¦ nflverse release
espn_nfl_schedule Β· espn_nfl_scoreboardESPN scoreboard/schedule (no auth)πŸ”΅ ESPN (secondary)
load_nfl_snap_countsWeekly snap counts & snap-share % per playerπŸ“¦ nflverse release
load_nfl_depth_chartsWeekly depth charts, one row per slotted playerπŸ“¦ nflverse release
load_nfl_scheduleGame results + lines, one row per gameπŸ“¦ nflverse release
load_nfl_draft_picksEvery draft pick + career value, one row per pickπŸ“¦ nflverse release
get_current_nfl_season · most_recent_nfl_seasonSeason helpers🟒 helper

πŸ”Œ Setup​

pip install sportsdataverse

No API key needed β€” the native api.nfl.com wrappers mint a fresh anonymous token for you, and the NextGen Stats client warms its own browser cookies. The nflverse loaders just read public release parquets. 😊

import polars as pl
import sportsdataverse.nfl as nfl

pl.Config.set_tbl_cols(8) # keep wide frames readable in the notebook

polars.config.Config

Native NFL.com / NextGen endpoints and ESPN are live services β€” great in-season, occasionally grumpy in the offseason or behind a flaky network. A tiny safe() helper runs each live call defensively: you get the frame when the feed is up, and a friendly one-liner when it isn't (never a scary traceback). πŸ›Ÿ The load_* release parquets are reliable, so we call those directly.

def safe(label, thunk):
"""Run a live call; return its result, or print a one-liner and return None."""
try:
out = thunk()
print(f"βœ… {label}")
return out
except Exception as e: # noqa: BLE001 -- demo resilience over network blips
print(f"⏭️ {label}: unavailable right now ({type(e).__name__})")
return None


# 2024 is a complete season with full data everywhere β€” a safe default to demo.
SEASON = 2024

🟒 Premium first: NFL.com native standings​

The headliner. nfl_standings returns one row per team with conference/division records, streaks, clinch flags, point differentials β€” the works. Pass season, season_type ("REG"/"POST"/"PRE" β€” strings, not ESPN's numeric codes) and week.

standings = safe(
"NFL.com standings",
lambda: nfl.nfl_standings(season=SEASON, season_type="REG", week=18),
)
standings.shape if standings is not None else "standings unavailable"

βœ… NFL.com standings





(32, 53)
cols = [
"team_full_name", "conference_rank", "division_rank",
"overall_wins", "overall_losses", "overall_ties",
"division_wins", "division_losses",
]
(standings.select([c for c in cols if c in standings.columns])
.sort("conference_rank")
.head(10)
if standings is not None else "standings unavailable")

shape: (10, 8)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ team_full_ ┆ conference ┆ division_r ┆ overall_w ┆ overall_l ┆ overall_t ┆ division_ ┆ division_ β”‚
β”‚ name ┆ _rank ┆ ank ┆ ins ┆ osses ┆ ies ┆ wins ┆ losses β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ str ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 ┆ i64 β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•ͺ════════════β•ͺ════════════β•ͺ═══════════β•ͺ═══════════β•ͺ═══════════β•ͺ═══════════β•ͺ═══════════║
β”‚ Detroit ┆ 1 ┆ 1 ┆ 15 ┆ 2 ┆ 0 ┆ 6 ┆ 0 β”‚
β”‚ Lions ┆ ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Kansas ┆ 1 ┆ 1 ┆ 15 ┆ 2 ┆ 0 ┆ 5 ┆ 1 β”‚
β”‚ City ┆ ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Chiefs ┆ ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Buffalo ┆ 2 ┆ 1 ┆ 13 ┆ 4 ┆ 0 ┆ 5 ┆ 1 β”‚
β”‚ Bills ┆ ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Philadelph ┆ 2 ┆ 1 ┆ 14 ┆ 3 ┆ 0 ┆ 5 ┆ 1 β”‚
β”‚ ia Eagles ┆ ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Baltimore ┆ 3 ┆ 1 ┆ 12 ┆ 5 ┆ 0 ┆ 4 ┆ 2 β”‚
β”‚ Ravens ┆ ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Tampa Bay ┆ 3 ┆ 1 ┆ 10 ┆ 7 ┆ 0 ┆ 4 ┆ 2 β”‚
β”‚ Buccaneers ┆ ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Houston ┆ 4 ┆ 1 ┆ 10 ┆ 7 ┆ 0 ┆ 5 ┆ 1 β”‚
β”‚ Texans ┆ ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Los ┆ 4 ┆ 1 ┆ 10 ┆ 7 ┆ 0 ┆ 4 ┆ 2 β”‚
β”‚ Angeles ┆ ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Rams ┆ ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Los ┆ 5 ┆ 2 ┆ 11 ┆ 6 ┆ 0 ┆ 4 ┆ 2 β”‚
β”‚ Angeles ┆ ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Chargers ┆ ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Minnesota ┆ 5 ┆ 2 ┆ 14 ┆ 3 ┆ 0 ┆ 4 ┆ 2 β”‚
β”‚ Vikings ┆ ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ‘₯ Rosters & the week calendar​

nfl_rosters gives one row per team for a season, with the player list nested under persons (great for a team directory). nfl_weeks is the season's week calendar β€” handy for finding bye weeks and date ranges before you loop over a slate.

FunctionOne row perKey columns
nfl_rostersteamteam_abbreviation, team_conference_abbr, persons
nfl_weeksweekweek, week_type, bye_teams, date_begin
rosters = safe("NFL.com rosters", lambda: nfl.nfl_rosters(season=SEASON))
cols = ["team_abbreviation", "team_full_name", "team_conference_abbr", "team_division_full_name"]
(rosters.select([c for c in cols if c in rosters.columns]).head(8)
if rosters is not None else "rosters unavailable")

βœ… NFL.com rosters





shape: (8, 4)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ team_abbreviation ┆ team_full_name ┆ team_conference_abbr ┆ team_division_full_name β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ str ┆ str ┆ str ┆ str β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ════════════════════β•ͺ══════════════════════β•ͺ═════════════════════════║
β”‚ ARI ┆ Arizona Cardinals ┆ NFC ┆ NFC West β”‚
β”‚ ATL ┆ Atlanta Falcons ┆ NFC ┆ NFC South β”‚
β”‚ BAL ┆ Baltimore Ravens ┆ AFC ┆ AFC North β”‚
β”‚ BUF ┆ Buffalo Bills ┆ AFC ┆ AFC East β”‚
β”‚ CAR ┆ Carolina Panthers ┆ NFC ┆ NFC South β”‚
β”‚ CHI ┆ Chicago Bears ┆ NFC ┆ NFC North β”‚
β”‚ CIN ┆ Cincinnati Bengals ┆ AFC ┆ AFC North β”‚
β”‚ CLE ┆ Cleveland Browns ┆ AFC ┆ AFC North β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
weeks = safe("NFL.com weeks", lambda: nfl.nfl_weeks(season=SEASON, season_type="REG"))
cols = ["season", "week", "week_type", "date_begin", "date_end", "bye_teams"]
(weeks.select([c for c in cols if c in weeks.columns]).head(8)
if weeks is not None else "weeks unavailable")

βœ… NFL.com weeks





shape: (8, 6)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ season ┆ week ┆ week_type ┆ date_begin ┆ date_end ┆ bye_teams β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ i64 ┆ i64 ┆ str ┆ str ┆ str ┆ str β”‚
β•žβ•β•β•β•β•β•β•β•β•ͺ══════β•ͺ═══════════β•ͺ════════════β•ͺ════════════β•ͺ═════════════════════════════════║
β”‚ 2024 ┆ 18 ┆ REG ┆ 2025-01-01 ┆ 2025-01-08 ┆ [] β”‚
β”‚ 2024 ┆ 17 ┆ REG ┆ 2024-12-24 ┆ 2025-01-01 ┆ [] β”‚
β”‚ 2024 ┆ 16 ┆ REG ┆ 2024-12-18 ┆ 2024-12-24 ┆ [] β”‚
β”‚ 2024 ┆ 15 ┆ REG ┆ 2024-12-11 ┆ 2024-12-18 ┆ [] β”‚
β”‚ 2024 ┆ 14 ┆ REG ┆ 2024-12-04 ┆ 2024-12-11 ┆ [{'id': '10400325-48de-3d6a-be… β”‚
β”‚ 2024 ┆ 13 ┆ REG ┆ 2024-11-27 ┆ 2024-12-04 ┆ [] β”‚
β”‚ 2024 ┆ 12 ┆ REG ┆ 2024-11-20 ┆ 2024-11-27 ┆ [{'id': '10400200-f401-4e53-51… β”‚
β”‚ 2024 ┆ 11 ┆ REG ┆ 2024-11-13 ┆ 2024-11-20 ┆ [{'id': '10403800-517c-7b8c-65… β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ₯ The weekly injury report​

nfl_injuries is the official weekly injury report β€” one row per listed player with their injury_status (Out / Doubtful / Questionable), practice participation, and team. This is the premium native feed, not a scrape.

inj = safe(
"NFL.com injuries",
lambda: nfl.nfl_injuries(season=SEASON, season_type="REG", week=1),
)
cols = [
"team_full_name", "person_display_name", "position",
"injuries", "injury_status", "practice_status",
]
(inj.select([c for c in cols if c in inj.columns]).head(10)
if inj is not None else "injuries unavailable")

βœ… NFL.com injuries





shape: (10, 6)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ team_full_name ┆ person_display_ ┆ position ┆ injuries ┆ injury_status ┆ practice_status β”‚
β”‚ --- ┆ name ┆ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ str ┆ --- ┆ str ┆ str ┆ str ┆ str β”‚
β”‚ ┆ str ┆ ┆ ┆ ┆ β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ═════════════════β•ͺ══════════β•ͺ═════════════════β•ͺ═══════════════β•ͺ═════════════════║
β”‚ Atlanta Falcons ┆ Ta'Quon Graham ┆ DE ┆ [] ┆ null ┆ LIMITED β”‚
β”‚ Atlanta Falcons ┆ Antonio ┆ CB ┆ ['Groin'] ┆ OUT ┆ DIDNOT β”‚
β”‚ ┆ Hamilton ┆ ┆ ┆ ┆ β”‚
β”‚ Atlanta Falcons ┆ Grady Jarrett ┆ DT ┆ [] ┆ null ┆ DIDNOT β”‚
β”‚ Atlanta Falcons ┆ Nate Landman ┆ LB ┆ [] ┆ null ┆ FULL β”‚
β”‚ Atlanta Falcons ┆ Chris Lindstrom ┆ G ┆ ['Evaluated for ┆ null ┆ null β”‚
β”‚ ┆ ┆ ┆ a possible hea… ┆ ┆ β”‚
β”‚ Atlanta Falcons ┆ Jake Matthews ┆ T ┆ [] ┆ null ┆ LIMITED β”‚
β”‚ Atlanta Falcons ┆ David Onyemata ┆ DT ┆ [] ┆ null ┆ DIDNOT β”‚
β”‚ Atlanta Falcons ┆ Kyle Pitts ┆ TE ┆ [] ┆ null ┆ FULL β”‚
β”‚ Baltimore ┆ Rasheen Ali ┆ RB ┆ ['Neck'] ┆ DOUBTFUL ┆ LIMITED β”‚
β”‚ Ravens ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Baltimore ┆ Adisa Isaac ┆ LB ┆ ['Hamstring'] ┆ OUT ┆ DIDNOT β”‚
β”‚ Ravens ┆ ┆ ┆ ┆ ┆ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“‹ Per-game details for a week​

Need the full slate with drive charts, broadcast info and embedded standings? nfl_weekly_game_details returns one row per game for a week (toggle the heavy blocks with the include_* flags). For live in-game state (clock, down & distance, red-zone flags), reach for nfl_game_summaries.

wgd = safe(
"NFL.com weekly game details",
lambda: nfl.nfl_weekly_game_details(season=SEASON, season_type="REG", week=1),
)
cols = ["week", "date", "game_type", "away_team_full_name", "home_team_full_name", "status"]
(wgd.select([c for c in cols if c in wgd.columns]).head(8)
if wgd is not None else "weekly game details unavailable")

βœ… NFL.com weekly game details





shape: (8, 6)
β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ week ┆ date ┆ game_type ┆ away_team_full_name ┆ home_team_full_name ┆ status β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ i64 ┆ str ┆ str ┆ str ┆ str ┆ str β”‚
β•žβ•β•β•β•β•β•β•ͺ════════════β•ͺ═════════════β•ͺ══════════════════════β•ͺ═════════════════════β•ͺ═══════════║
β”‚ 1 ┆ 2024-09-06 ┆ UNSPECIFIED ┆ Baltimore Ravens ┆ Kansas City Chiefs ┆ SCHEDULED β”‚
β”‚ 1 ┆ 2024-09-07 ┆ UNSPECIFIED ┆ Green Bay Packers ┆ Philadelphia Eagles ┆ SCHEDULED β”‚
β”‚ 1 ┆ 2024-09-08 ┆ UNSPECIFIED ┆ Pittsburgh Steelers ┆ Atlanta Falcons ┆ SCHEDULED β”‚
β”‚ 1 ┆ 2024-09-08 ┆ UNSPECIFIED ┆ Arizona Cardinals ┆ Buffalo Bills ┆ SCHEDULED β”‚
β”‚ 1 ┆ 2024-09-08 ┆ UNSPECIFIED ┆ Tennessee Titans ┆ Chicago Bears ┆ SCHEDULED β”‚
β”‚ 1 ┆ 2024-09-08 ┆ UNSPECIFIED ┆ New England Patriots ┆ Cincinnati Bengals ┆ SCHEDULED β”‚
β”‚ 1 ┆ 2024-09-08 ┆ UNSPECIFIED ┆ Houston Texans ┆ Indianapolis Colts ┆ SCHEDULED β”‚
β”‚ 1 ┆ 2024-09-08 ┆ UNSPECIFIED ┆ Jacksonville Jaguars ┆ Miami Dolphins ┆ SCHEDULED β”‚
β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

⚑ NextGen Stats: the tracking layer​

This is where it gets fun. The NFL's NextGen Stats API exposes player-tracking metrics you won't find in a box score β€” time to throw, completion percentage over expectation (CPOE), separation, ball-carrier top speed. All token-free.

nfl_ngs_statboard is the season leaderboard. Ask for stat_type "passing", "rushing", or "receiving".

qb = safe(
"NGS passing statboard",
lambda: nfl.nfl_ngs_statboard(stat_type="passing", season=SEASON, season_type="REG"),
)
cols = [
"playerName", "passerRating", "completionPercentageAboveExpectation",
"avgTimeToThrow", "aggressiveness", "passYards", "passTouchdowns",
]
(qb.select([c for c in cols if c in qb.columns])
.sort("passerRating", descending=True)
.head(10)
if qb is not None else "NGS statboard unavailable")

βœ… NGS passing statboard





shape: (10, 7)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ playerName ┆ passerRating ┆ completionPe ┆ avgTimeToTh ┆ aggressiven ┆ passYards ┆ passTouchdo β”‚
β”‚ --- ┆ --- ┆ rcentageAbov ┆ row ┆ ess ┆ --- ┆ wns β”‚
β”‚ str ┆ f64 ┆ eExpec… ┆ --- ┆ --- ┆ i64 ┆ --- β”‚
β”‚ ┆ ┆ --- ┆ f64 ┆ f64 ┆ ┆ i64 β”‚
β”‚ ┆ ┆ f64 ┆ ┆ ┆ ┆ β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ══════════════β•ͺ══════════════β•ͺ═════════════β•ͺ═════════════β•ͺ═══════════β•ͺ═════════════║
β”‚ Lamar ┆ 119.629044 ┆ -0.194641 ┆ 3.144269 ┆ 10.970464 ┆ 4172 ┆ 41 β”‚
β”‚ Jackson ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Jared Goff ┆ 111.769481 ┆ 5.054879 ┆ 2.787295 ┆ 11.502783 ┆ 4629 ┆ 37 β”‚
β”‚ Joe Burrow ┆ 108.537832 ┆ 4.521902 ┆ 2.710951 ┆ 15.797546 ┆ 4918 ┆ 43 β”‚
β”‚ Baker ┆ 106.761696 ┆ 2.17107 ┆ 2.6982 ┆ 10.877193 ┆ 4500 ┆ 41 β”‚
β”‚ Mayfield ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Jalen Hurts ┆ 103.687673 ┆ 6.305457 ┆ 3.1313 ┆ 16.34349 ┆ 2903 ┆ 18 β”‚
β”‚ Sam Darnold ┆ 102.534404 ┆ 2.799064 ┆ 3.083395 ┆ 13.944954 ┆ 4319 ┆ 35 β”‚
β”‚ Justin ┆ 101.703042 ┆ 2.391567 ┆ 2.910539 ┆ 15.873016 ┆ 3870 ┆ 23 β”‚
β”‚ Herbert ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Josh Allen ┆ 101.384576 ┆ 0.781139 ┆ 2.886884 ┆ 16.770186 ┆ 3731 ┆ 28 β”‚
β”‚ Tua ┆ 101.362782 ┆ 1.696667 ┆ 2.416476 ┆ 12.280702 ┆ 2867 ┆ 19 β”‚
β”‚ Tagovailoa ┆ ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ Derek Carr ┆ 101.022999 ┆ 3.222724 ┆ 2.758918 ┆ 11.111111 ┆ 2145 ┆ 15 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

And nfl_ngs_leaders serves the highlight-reel top-N boards β€” each row is the play that earned the leader their spot. Categories include "speed" (fastest ball carriers), "yac_season" (yards-after-catch over expected), "completion_season" (most-improbable completions) and more.

fast = safe(
"NGS fastest ball carriers",
lambda: nfl.nfl_ngs_leaders(category="speed", season=SEASON, season_type="REG"),
)
cols = ["leader_playerName", "leader_teamAbbr", "leader_maxSpeed", "leader_yards", "play_playDescription"]
(fast.select([c for c in cols if c in fast.columns]).head(8)
if fast is not None else "NGS leaders unavailable")

βœ… NGS fastest ball carriers





shape: (8, 5)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ leader_playerName ┆ leader_teamAbbr ┆ leader_maxSpeed ┆ leader_yards ┆ play_playDescription β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ str ┆ str ┆ f64 ┆ i64 ┆ str β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ═════════════════β•ͺ═════════════════β•ͺ══════════════β•ͺ═══════════════════════════║
β”‚ KaVontae Turpin ┆ DAL ┆ 22.356818 ┆ 64 ┆ (15:00) (Shotgun) C.Rush β”‚
β”‚ ┆ ┆ ┆ ┆ pass … β”‚
β”‚ Brian Thomas Jr. ┆ JAX ┆ 22.152273 ┆ 85 ┆ (7:12) (Shotgun) β”‚
β”‚ ┆ ┆ ┆ ┆ T.Lawrence pa… β”‚
β”‚ Jahmyr Gibbs ┆ DET ┆ 22.029546 ┆ 70 ┆ (4:07) (Shotgun) J.Gibbs β”‚
β”‚ ┆ ┆ ┆ ┆ left … β”‚
β”‚ Saquon Barkley ┆ PHI ┆ 21.927273 ┆ 55 ┆ (11:04) (No Huddle, β”‚
β”‚ ┆ ┆ ┆ ┆ Shotgun) S… β”‚
β”‚ Saquon Barkley ┆ PHI ┆ 21.906818 ┆ 72 ┆ (2:54) (Shotgun) β”‚
β”‚ ┆ ┆ ┆ ┆ S.Barkley lef… β”‚
β”‚ Nico Collins ┆ HOU ┆ 21.886364 ┆ 55 ┆ (12:56) C.Stroud pass β”‚
β”‚ ┆ ┆ ┆ ┆ deep mid… β”‚
β”‚ James Cook ┆ BUF ┆ 21.845455 ┆ 65 ┆ (8:48) A.Anderson β”‚
β”‚ ┆ ┆ ┆ ┆ reported in … β”‚
β”‚ KaVontae Turpin ┆ DAL ┆ 21.845455 ┆ 18 ┆ J.Elliott kicks 60 yards β”‚
β”‚ ┆ ┆ ┆ ┆ from … β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“¦ nflverse loaders: the bulk-data workhorses​

For full-season modelling you want the nflverse release parquets β€” the exact same assets that power nflfastR / nflreadr / nflreadpy. These are versioned, cached releases (very reliable), so we call them directly.

FunctionRowsHighlights
load_nfl_pbp~49k/seasonEPA, WP, air yards, 370+ columns
load_nfl_player_statsweeklypassing/rushing/receiving box lines
load_nfl_nextgen_statsweeklyNGS back to 2016
load_nfl_rostersper playerIDs, bios, draft info
pbp = nfl.load_nfl_pbp([SEASON])
pbp.shape

(49492, 372)
(pbp
.filter(pl.col("play_type").is_not_null())
.select(["game_id", "qtr", "down", "ydstogo", "posteam", "play_type", "yards_gained", "epa", "desc"])
.head(8))

shape: (8, 9)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ game_id ┆ qtr ┆ down ┆ ydstogo ┆ … ┆ play_type ┆ yards_gained ┆ epa ┆ desc β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- ┆ ┆ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ str ┆ f64 ┆ f64 ┆ f64 ┆ ┆ str ┆ f64 ┆ f64 ┆ str β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ═════β•ͺ══════β•ͺ═════════β•ͺ═══β•ͺ═══════════β•ͺ══════════════β•ͺ═══════════β•ͺ═══════════════║
β”‚ 2024_01_ARI_BU ┆ 1.0 ┆ null ┆ 0.0 ┆ … ┆ kickoff ┆ 0.0 ┆ 0.257819 ┆ 2-T.Bass β”‚
β”‚ F ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ kicks 65 β”‚
β”‚ ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ yards from B… β”‚
β”‚ 2024_01_ARI_BU ┆ 1.0 ┆ 1.0 ┆ 10.0 ┆ … ┆ run ┆ 3.0 ┆ -0.200602 ┆ (15:00) β”‚
β”‚ F ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ 6-J.Conner up β”‚
β”‚ ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ the midd… β”‚
β”‚ 2024_01_ARI_BU ┆ 1.0 ┆ 2.0 ┆ 7.0 ┆ … ┆ pass ┆ 22.0 ┆ 2.028874 ┆ (14:27) β”‚
β”‚ F ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ 1-K.Murray β”‚
β”‚ ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ pass short … β”‚
β”‚ 2024_01_ARI_BU ┆ 1.0 ┆ 1.0 ┆ 10.0 ┆ … ┆ pass ┆ 9.0 ┆ 0.754242 ┆ (13:43) β”‚
β”‚ F ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ (Shotgun) β”‚
β”‚ ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ 1-K.Murray p… β”‚
β”‚ 2024_01_ARI_BU ┆ 1.0 ┆ 2.0 ┆ 1.0 ┆ … ┆ run ┆ 2.0 ┆ -0.029602 ┆ (13:02) β”‚
β”‚ F ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ 6-J.Conner up β”‚
β”‚ ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ the midd… β”‚
β”‚ 2024_01_ARI_BU ┆ 1.0 ┆ 1.0 ┆ 10.0 ┆ … ┆ run ┆ 2.0 ┆ -0.247749 ┆ (12:26) β”‚
β”‚ F ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ (Shotgun) β”‚
β”‚ ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ 6-J.Conner l… β”‚
β”‚ 2024_01_ARI_BU ┆ 1.0 ┆ 2.0 ┆ 8.0 ┆ … ┆ run ┆ 2.0 ┆ -0.530139 ┆ (11:51) β”‚
β”‚ F ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ 6-J.Conner β”‚
β”‚ ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ left end to… β”‚
β”‚ 2024_01_ARI_BU ┆ 1.0 ┆ 3.0 ┆ 6.0 ┆ … ┆ pass ┆ 8.0 ┆ 1.6808 ┆ (11:08) β”‚
β”‚ F ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ (Shotgun) β”‚
β”‚ ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ 1-K.Murray p… β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
ngs_release = nfl.load_nfl_nextgen_stats([SEASON], stat_type="passing")
(ngs_release
.filter(pl.col("week") == 0) # week 0 == season totals in this release
.select(["player_display_name", "team_abbr", "attempts", "pass_yards",
"completion_percentage_above_expectation", "passer_rating"])
.sort("passer_rating", descending=True)
.head(8))

shape: (8, 6)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ player_display_name ┆ team_abbr ┆ attempts ┆ pass_yards ┆ completion_percentage_ ┆ passer_rating β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- ┆ above_ex… ┆ --- β”‚
β”‚ str ┆ str ┆ i32 ┆ i32 ┆ --- ┆ f64 β”‚
β”‚ ┆ ┆ ┆ ┆ f64 ┆ β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ═══════════β•ͺ══════════β•ͺ════════════β•ͺ════════════════════════β•ͺ═══════════════║
β”‚ Lamar Jackson ┆ BAL ┆ 474 ┆ 4172 ┆ -0.194641 ┆ 119.629044 β”‚
β”‚ Jared Goff ┆ DET ┆ 539 ┆ 4629 ┆ 5.054879 ┆ 111.769481 β”‚
β”‚ Joe Burrow ┆ CIN ┆ 652 ┆ 4918 ┆ 4.521902 ┆ 108.537832 β”‚
β”‚ Baker Mayfield ┆ TB ┆ 570 ┆ 4500 ┆ 2.17107 ┆ 106.761696 β”‚
β”‚ Jalen Hurts ┆ PHI ┆ 361 ┆ 2903 ┆ 6.305457 ┆ 103.687673 β”‚
β”‚ Sam Darnold ┆ MIN ┆ 545 ┆ 4319 ┆ 2.799064 ┆ 102.534404 β”‚
β”‚ Justin Herbert ┆ LAC ┆ 504 ┆ 3870 ┆ 2.391567 ┆ 101.703042 β”‚
β”‚ Josh Allen ┆ BUF ┆ 483 ┆ 3731 ┆ 0.781139 ┆ 101.384576 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ”΅ Secondary path: ESPN (quick & no-auth)​

When you just want a fast scoreboard without minting a token, ESPN is right there. espn_nfl_schedule returns a tidy schedule frame; pass dates=YYYYMMDD for a single day. (There's also a raw espn_nfl_scoreboard if you want the unparsed JSON.)

espn_sched = safe("ESPN schedule", lambda: nfl.espn_nfl_schedule(dates=20240908))
cols = ["id", "away_display_name", "home_display_name", "away_score", "home_score", "status_type_description"]
(espn_sched.select([c for c in cols if c in espn_sched.columns]).head(8)
if espn_sched is not None else "ESPN schedule unavailable")

βœ… ESPN schedule





shape: (8, 6)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ id ┆ away_display_name ┆ home_display_name ┆ away_score ┆ home_score ┆ status_type_descr β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ iption β”‚
β”‚ str ┆ str ┆ str ┆ str ┆ str ┆ --- β”‚
β”‚ ┆ ┆ ┆ ┆ ┆ str β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•ͺ════════════════════β•ͺ═══════════════════β•ͺ════════════β•ͺ════════════β•ͺ═══════════════════║
β”‚ 401671744 ┆ Pittsburgh ┆ Atlanta Falcons ┆ 18 ┆ 10 ┆ Final β”‚
β”‚ ┆ Steelers ┆ ┆ ┆ ┆ β”‚
β”‚ 401671617 ┆ Arizona Cardinals ┆ Buffalo Bills ┆ 28 ┆ 34 ┆ Final β”‚
β”‚ 401671719 ┆ Tennessee Titans ┆ Chicago Bears ┆ 17 ┆ 24 ┆ Final β”‚
β”‚ 401671628 ┆ New England ┆ Cincinnati ┆ 16 ┆ 10 ┆ Final β”‚
β”‚ ┆ Patriots ┆ Bengals ┆ ┆ ┆ β”‚
β”‚ 401671861 ┆ Houston Texans ┆ Indianapolis ┆ 29 ┆ 27 ┆ Final β”‚
β”‚ ┆ ┆ Colts ┆ ┆ ┆ β”‚
β”‚ 401671849 ┆ Jacksonville ┆ Miami Dolphins ┆ 17 ┆ 20 ┆ Final β”‚
β”‚ ┆ Jaguars ┆ ┆ ┆ ┆ β”‚
β”‚ 401671734 ┆ Carolina Panthers ┆ New Orleans ┆ 10 ┆ 47 ┆ Final β”‚
β”‚ ┆ ┆ Saints ┆ ┆ ┆ β”‚
β”‚ 401671712 ┆ Minnesota Vikings ┆ New York Giants ┆ 28 ┆ 6 ┆ Final β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

🍳 Cookbook: common NFL tasks​

A full dozen recipes you'll reach for constantly β€” leaderboards, splits, team-level efficiency, snap-share workhorses, play-by-play slices, a schedule scan, and a quick hop into pandas. Each one leans on the premium native/NextGen feeds or the rock-solid nflverse release parquets, and every live call is wrapped so a network blip never breaks your run. Recipes 1–4 use the live NFL.com / NextGen endpoints; 5–12 build on the cached release parquets, so they run anywhere, anytime. 🏈

Recipe 1 β€” This week's "Out" list πŸš‘β€‹

Filter the official injury report down to players ruled Out β€” exactly what you'd check before setting a lineup.

rep = safe(
"injury report",
lambda: nfl.nfl_injuries(season=SEASON, season_type="REG", week=1),
)
if rep is not None and rep.height and "injury_status" in rep.columns:
out = (
rep.filter(pl.col("injury_status").str.to_lowercase() == "out")
.select([c for c in ["team_full_name", "person_display_name", "position", "injuries"]
if c in rep.columns])
.head(15)
)
else:
out = "injury report unavailable"
out

βœ… injury report





shape: (5, 4)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ team_full_name ┆ person_display_name ┆ position ┆ injuries β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ str ┆ str ┆ str ┆ str β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ═════════════════════β•ͺ══════════β•ͺ═════════════════════════════════║
β”‚ Atlanta Falcons ┆ Antonio Hamilton ┆ CB ┆ ['Groin'] β”‚
β”‚ Baltimore Ravens ┆ Adisa Isaac ┆ LB ┆ ['Hamstring'] β”‚
β”‚ Baltimore Ravens ┆ Kyle Van Noy ┆ LB ┆ ['gameday concussion protocol … β”‚
β”‚ Buffalo Bills ┆ Taron Johnson ┆ CB ┆ ['Forearm'] β”‚
β”‚ Buffalo Bills ┆ Javon Solomon ┆ DE ┆ ['Oblique'] β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Recipe 2 β€” CPOE leaderboard from NextGen Stats πŸŽ―β€‹

Who's beating expectation as a passer? Rank qualified QBs by completion percentage above expectation straight off the NextGen statboard.

board = safe(
"NGS passing board",
lambda: nfl.nfl_ngs_statboard(stat_type="passing", season=SEASON, season_type="REG"),
)
if board is not None and board.height and "completionPercentageAboveExpectation" in board.columns:
cpoe = (
board.filter(pl.col("attempts") >= 200)
.select(["playerName", "attempts", "completionPercentage",
"completionPercentageAboveExpectation", "passerRating"])
.sort("completionPercentageAboveExpectation", descending=True)
.head(10)
)
else:
cpoe = "NGS board unavailable"
cpoe

βœ… NGS passing board





shape: (10, 5)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ playerName ┆ attempts ┆ completionPercentage ┆ completionPercentageAboveExpec ┆ passerRating β”‚
β”‚ --- ┆ --- ┆ --- ┆ … ┆ --- β”‚
β”‚ str ┆ i64 ┆ f64 ┆ --- ┆ f64 β”‚
β”‚ ┆ ┆ ┆ f64 ┆ β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ══════════β•ͺ══════════════════════β•ͺ════════════════════════════════β•ͺ══════════════║
β”‚ Jalen Hurts ┆ 361 ┆ 68.698061 ┆ 6.305457 ┆ 103.687673 β”‚
β”‚ Jared Goff ┆ 539 ┆ 72.356215 ┆ 5.054879 ┆ 111.769481 β”‚
β”‚ Joe Burrow ┆ 652 ┆ 70.552147 ┆ 4.521902 ┆ 108.537832 β”‚
β”‚ Geno Smith ┆ 578 ┆ 70.415225 ┆ 3.854464 ┆ 93.202134 β”‚
β”‚ Kirk Cousins ┆ 453 ┆ 66.887417 ┆ 3.442009 ┆ 88.61755 β”‚
β”‚ Derek Carr ┆ 279 ┆ 67.741935 ┆ 3.222724 ┆ 101.022999 β”‚
β”‚ Drake Maye ┆ 338 ┆ 66.568047 ┆ 2.986893 ┆ 88.079389 β”‚
β”‚ Brock Purdy ┆ 455 ┆ 65.934066 ┆ 2.983077 ┆ 96.076007 β”‚
β”‚ Sam Darnold ┆ 545 ┆ 66.238532 ┆ 2.799064 ┆ 102.534404 β”‚
β”‚ Justin Herbert ┆ 504 ┆ 65.873016 ┆ 2.391567 ┆ 101.703042 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Recipe 3 β€” Standings β†’ division winners πŸ†β€‹

Take the premium standings and pull the team that tops each division. One group-by and you've got your playoff-seeding cheat sheet.

st = safe(
"standings",
lambda: nfl.nfl_standings(season=SEASON, season_type="REG", week=18),
)
if st is not None and st.height and {"division_rank", "team_full_name"}.issubset(st.columns):
div_col = next((c for c in ["team_division_full_name", "division_full_name", "division"] if c in st.columns), None)
keep = [c for c in [div_col, "team_full_name", "overall_wins", "overall_losses"] if c]
winners = (
st.filter(pl.col("division_rank") == 1)
.select(keep)
.sort(div_col) if div_col else st.filter(pl.col("division_rank") == 1).select(keep)
)
else:
winners = "standings unavailable"
winners

βœ… standings







shape: (8, 3)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ team_full_name ┆ overall_wins ┆ overall_losses β”‚
β”‚ --- ┆ --- ┆ --- β”‚
β”‚ str ┆ i64 ┆ i64 β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ══════════════β•ͺ════════════════║
β”‚ Baltimore Ravens ┆ 12 ┆ 5 β”‚
β”‚ Buffalo Bills ┆ 13 ┆ 4 β”‚
β”‚ Detroit Lions ┆ 15 ┆ 2 β”‚
β”‚ Houston Texans ┆ 10 ┆ 7 β”‚
β”‚ Kansas City Chiefs ┆ 15 ┆ 2 β”‚
β”‚ Los Angeles Rams ┆ 10 ┆ 7 β”‚
β”‚ Philadelphia Eagles ┆ 14 ┆ 3 β”‚
β”‚ Tampa Bay Buccaneers ┆ 10 ┆ 7 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Recipe 4 β€” A game's NextGen passer splits πŸ”¬β€‹

Grab an NGS gameId from the schedule, then pull nfl_ngs_gamecenter_overview to see each side's primary passer with tracking-derived splits.

sched = safe(
"NGS schedule",
lambda: nfl.nfl_ngs_league_schedule(season=SEASON, season_type="REG", week=1),
)
if sched is not None and sched.height and "gameId" in sched.columns:
gid = sched["gameId"][0]
ov = safe(f"NGS gamecenter {gid}",
lambda: nfl.nfl_ngs_gamecenter_overview(game_id=gid, group="passers"))
if ov is not None and ov.height:
out = ov.select([c for c in ["side", "teamAbbr", "playerName", "position",
"completions", "attempts", "passYards", "touchdowns"]
if c in ov.columns])
else:
out = "gamecenter unavailable"
else:
out = "NGS schedule unavailable"
out

βœ… NGS schedule


βœ… NGS gamecenter 2024090500





shape: (2, 8)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ side ┆ teamAbbr ┆ playerName ┆ position ┆ completions ┆ attempts ┆ passYards ┆ touchdowns β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ str ┆ str ┆ str ┆ str ┆ i64 ┆ i64 ┆ i64 ┆ i64 β”‚
β•žβ•β•β•β•β•β•β•β•β•β•ͺ══════════β•ͺ════════════════β•ͺ══════════β•ͺ═════════════β•ͺ══════════β•ͺ═══════════β•ͺ════════════║
β”‚ home ┆ KC ┆ Patrick ┆ QB ┆ 20 ┆ 28 ┆ 291 ┆ 1 β”‚
β”‚ ┆ ┆ Mahomes ┆ ┆ ┆ ┆ ┆ β”‚
β”‚ visitor ┆ BAL ┆ Lamar Jackson ┆ QB ┆ 26 ┆ 41 ┆ 273 ┆ 1 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Recipe 5 β€” Season rushing leaders πŸƒβ€‹

Roll the weekly box scores in load_nfl_player_stats up to season totals and crown the ground-game kings (β‰₯150 carries).

ps = nfl.load_nfl_player_stats()
rush_cols = {"season", "season_type", "carries", "rushing_yards"}
if rush_cols.issubset(ps.columns):
rush_lb = (
ps.filter((pl.col("season") == SEASON) & (pl.col("season_type") == "REG"))
.group_by(["player_display_name", "recent_team"])
.agg(
pl.col("carries").sum().alias("carries"),
pl.col("rushing_yards").sum().alias("rush_yds"),
pl.col("rushing_tds").sum().alias("rush_td"),
pl.col("rushing_epa").sum().round(1).alias("rush_epa"),
)
.filter(pl.col("carries") >= 150)
.sort("rush_yds", descending=True)
.head(10)
)
else:
rush_lb = "player_stats schema changed β€” rushing columns missing"
rush_lb
shape: (10, 6)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ player_display_name ┆ recent_team ┆ carries ┆ rush_yds ┆ rush_td ┆ rush_epa β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ str ┆ str ┆ i32 ┆ f64 ┆ i32 ┆ f64 β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ═════════════β•ͺ═════════β•ͺ══════════β•ͺ═════════β•ͺ══════════║
β”‚ Saquon Barkley ┆ PHI ┆ 345 ┆ 2005.0 ┆ 13 ┆ 34.1 β”‚
β”‚ Derrick Henry ┆ BAL ┆ 325 ┆ 1921.0 ┆ 16 ┆ 40.6 β”‚
β”‚ Bijan Robinson ┆ ATL ┆ 304 ┆ 1456.0 ┆ 14 ┆ 16.9 β”‚
β”‚ Jonathan Taylor ┆ IND ┆ 303 ┆ 1431.0 ┆ 11 ┆ -21.0 β”‚
β”‚ Jahmyr Gibbs ┆ DET ┆ 250 ┆ 1412.0 ┆ 16 ┆ 35.1 β”‚
β”‚ Josh Jacobs ┆ GB ┆ 301 ┆ 1329.0 ┆ 15 ┆ -18.3 β”‚
β”‚ Kyren Williams ┆ LA ┆ 316 ┆ 1299.0 ┆ 14 ┆ -23.5 β”‚
β”‚ Chuba Hubbard ┆ CAR ┆ 250 ┆ 1195.0 ┆ 10 ┆ 9.7 β”‚
β”‚ Aaron Jones ┆ MIN ┆ 255 ┆ 1138.0 ┆ 5 ┆ -13.4 β”‚
β”‚ Bucky Irving ┆ TB ┆ 207 ┆ 1122.0 ┆ 8 ┆ 20.9 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Recipe 6 β€” The most efficient offenses (EPA/play) πŸ“ˆβ€‹

Expected points added is the modeller's favourite efficiency yardstick. Average epa over every run/pass in load_nfl_pbp to rank offenses.

pbp = nfl.load_nfl_pbp([SEASON])
if {"epa", "posteam", "play_type"}.issubset(pbp.columns):
epa_off = (
pbp.filter(pl.col("play_type").is_in(["run", "pass"]))
.group_by("posteam")
.agg(
pl.col("epa").mean().round(3).alias("epa_per_play"),
pl.len().alias("plays"),
)
.filter(pl.col("posteam").is_not_null())
.sort("epa_per_play", descending=True)
.head(10)
)
else:
epa_off = "pbp schema changed β€” epa/posteam columns missing"
epa_off
shape: (10, 3)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”
β”‚ posteam ┆ epa_per_play ┆ plays β”‚
β”‚ --- ┆ --- ┆ --- β”‚
β”‚ str ┆ f64 ┆ u32 β”‚
β•žβ•β•β•β•β•β•β•β•β•β•ͺ══════════════β•ͺ═══════║
β”‚ BAL ┆ 0.219 ┆ 1167 β”‚
β”‚ BUF ┆ 0.19 ┆ 1202 β”‚
β”‚ DET ┆ 0.165 ┆ 1164 β”‚
β”‚ WAS ┆ 0.132 ┆ 1308 β”‚
β”‚ TB ┆ 0.129 ┆ 1128 β”‚
β”‚ PHI ┆ 0.119 ┆ 1341 β”‚
β”‚ CIN ┆ 0.09 ┆ 1069 β”‚
β”‚ GB ┆ 0.071 ┆ 1081 β”‚
β”‚ SF ┆ 0.069 ┆ 1014 β”‚
β”‚ ARI ┆ 0.067 ┆ 1031 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”˜

Recipe 7 β€” Third-down conversion kings πŸ”‘β€‹

Move-the-chains efficiency: keep only 3rd-down run/pass snaps and divide conversions by attempts per offense β€” a classic play-by-play split.

if {"down", "third_down_converted", "posteam"}.issubset(pbp.columns):
third = (
pbp.filter((pl.col("down") == 3) & (pl.col("play_type").is_in(["run", "pass"])))
.group_by("posteam")
.agg(
pl.col("third_down_converted").sum().alias("conversions"),
pl.len().alias("attempts"),
)
.filter(pl.col("posteam").is_not_null())
.with_columns((pl.col("conversions") / pl.col("attempts") * 100).round(1).alias("conv_pct"))
.sort("conv_pct", descending=True)
.head(10)
)
else:
third = "pbp schema changed β€” third-down columns missing"
third
shape: (10, 4)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ posteam ┆ conversions ┆ attempts ┆ conv_pct β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ str ┆ f64 ┆ u32 ┆ f64 β”‚
β•žβ•β•β•β•β•β•β•β•β•β•ͺ═════════════β•ͺ══════════β•ͺ══════════║
β”‚ BAL ┆ 109.0 ┆ 214 ┆ 50.9 β”‚
β”‚ TB ┆ 115.0 ┆ 226 ┆ 50.9 β”‚
β”‚ KC ┆ 123.0 ┆ 255 ┆ 48.2 β”‚
β”‚ DET ┆ 101.0 ┆ 213 ┆ 47.4 β”‚
β”‚ CIN ┆ 100.0 ┆ 214 ┆ 46.7 β”‚
β”‚ BUF ┆ 107.0 ┆ 237 ┆ 45.1 β”‚
β”‚ WAS ┆ 118.0 ┆ 262 ┆ 45.0 β”‚
β”‚ ARI ┆ 83.0 ┆ 192 ┆ 43.2 β”‚
β”‚ SF ┆ 84.0 ┆ 196 ┆ 42.9 β”‚
β”‚ PHI ┆ 114.0 ┆ 278 ┆ 41.0 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Recipe 8 β€” Red-zone touchdown efficiency πŸŽ―β€‹

Filter the play-by-play to snaps inside the opponent's 20 (yardline_100 ≀ 20) and see which offenses actually punch it in instead of settling for three.

if {"yardline_100", "touchdown", "posteam"}.issubset(pbp.columns):
redzone = (
pbp.filter((pl.col("yardline_100") <= 20) & (pl.col("play_type").is_in(["run", "pass"])))
.group_by("posteam")
.agg(
pl.col("touchdown").sum().alias("rz_tds"),
pl.len().alias("rz_plays"),
)
.filter((pl.col("posteam").is_not_null()) & (pl.col("rz_plays") >= 80))
.with_columns((pl.col("rz_tds") / pl.col("rz_plays") * 100).round(1).alias("td_pct"))
.sort("td_pct", descending=True)
.head(10)
)
else:
redzone = "pbp schema changed β€” red-zone columns missing"
redzone
shape: (10, 4)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ posteam ┆ rz_tds ┆ rz_plays ┆ td_pct β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ str ┆ f64 ┆ u32 ┆ f64 β”‚
β•žβ•β•β•β•β•β•β•β•β•β•ͺ════════β•ͺ══════════β•ͺ════════║
β”‚ BAL ┆ 55.0 ┆ 178 ┆ 30.9 β”‚
β”‚ TB ┆ 47.0 ┆ 175 ┆ 26.9 β”‚
β”‚ BUF ┆ 55.0 ┆ 232 ┆ 23.7 β”‚
β”‚ DEN ┆ 36.0 ┆ 157 ┆ 22.9 β”‚
β”‚ DET ┆ 54.0 ┆ 239 ┆ 22.6 β”‚
β”‚ GB ┆ 43.0 ┆ 195 ┆ 22.1 β”‚
β”‚ SEA ┆ 27.0 ┆ 125 ┆ 21.6 β”‚
β”‚ NO ┆ 25.0 ┆ 120 ┆ 20.8 β”‚
β”‚ NYJ ┆ 31.0 ┆ 150 ┆ 20.7 β”‚
β”‚ WAS ┆ 52.0 ┆ 255 ┆ 20.4 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Recipe 9 β€” Snap-share workhorse running backs πŸ΄β€‹

load_nfl_snap_counts carries offense_pct per game β€” average it to find the backs their teams simply would not take off the field.

snaps = nfl.load_nfl_snap_counts([SEASON])
if {"position", "offense_pct", "player"}.issubset(snaps.columns):
workhorses = (
snaps.filter(pl.col("position") == "RB")
.group_by(["player", "team"])
.agg(
(pl.col("offense_pct").mean() * 100).round(1).alias("avg_snap_pct"),
pl.len().alias("games"),
)
.filter(pl.col("games") >= 10)
.sort("avg_snap_pct", descending=True)
.head(10)
)
else:
workhorses = "snap_counts schema changed β€” offense_pct/position missing"
workhorses
shape: (10, 4)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”
β”‚ player ┆ team ┆ avg_snap_pct ┆ games β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ str ┆ str ┆ f64 ┆ u32 β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ══════β•ͺ══════════════β•ͺ═══════║
β”‚ Kyren Williams ┆ LA ┆ 87.2 ┆ 18 β”‚
β”‚ Jonathan Taylor ┆ IND ┆ 80.3 ┆ 14 β”‚
β”‚ Chuba Hubbard ┆ CAR ┆ 77.3 ┆ 15 β”‚
β”‚ Bijan Robinson ┆ ATL ┆ 75.4 ┆ 17 β”‚
β”‚ Saquon Barkley ┆ PHI ┆ 75.1 ┆ 20 β”‚
β”‚ Breece Hall ┆ NYJ ┆ 72.4 ┆ 16 β”‚
β”‚ Alvin Kamara ┆ NO ┆ 70.9 ┆ 14 β”‚
β”‚ Tony Pollard ┆ TEN ┆ 67.8 ┆ 16 β”‚
β”‚ D'Andre Swift ┆ CHI ┆ 66.8 ┆ 17 β”‚
β”‚ Aaron Jones ┆ MIN ┆ 63.7 ┆ 18 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”˜

Recipe 10 β€” Receiving leaders, then a hop into pandas πŸΌβ€‹

Aggregate the receiving box lines, .to_pandas(), and add derived columns (yards-per-catch, catch rate) with familiar pandas syntax — the polars→pandas handoff is one method call.

rec_cols = {"receptions", "receiving_yards", "targets", "season", "season_type"}
if rec_cols.issubset(ps.columns):
rec = (
ps.filter((pl.col("season") == SEASON) & (pl.col("season_type") == "REG"))
.group_by(["player_display_name", "recent_team"])
.agg(
pl.col("receptions").sum().alias("rec"),
pl.col("receiving_yards").sum().alias("rec_yds"),
pl.col("targets").sum().alias("tgt"),
)
.filter(pl.col("rec") >= 70)
)
pdf = rec.to_pandas() # <-- polars -> pandas in one call
pdf["yards_per_rec"] = (pdf["rec_yds"] / pdf["rec"]).round(1)
pdf["catch_rate"] = (pdf["rec"] / pdf["tgt"] * 100).round(1)
rec_out = pdf.sort_values("rec_yds", ascending=False).head(10)[
["player_display_name", "recent_team", "rec", "rec_yds", "yards_per_rec", "catch_rate"]
].reset_index(drop=True)
else:
rec_out = "player_stats schema changed β€” receiving columns missing"
rec_out
player_display_name recent_team rec rec_yds yards_per_rec catch_rate
0 Ja'Marr Chase CIN 127 1708.0 13.4 72.6
1 Justin Jefferson MIN 103 1533.0 14.9 66.9
2 Brian Thomas JAX 87 1282.0 14.7 65.4
3 Drake London ATL 100 1271.0 12.7 63.3
4 Amon-Ra St. Brown DET 115 1263.0 11.0 81.6
5 Jerry Jeudy CLE 90 1229.0 13.7 62.1
6 Malik Nabers NYG 109 1204.0 11.0 64.1
7 CeeDee Lamb DAL 101 1194.0 11.8 66.4
8 Brock Bowers LV 112 1194.0 10.7 73.2
9 Ladd McConkey LAC 82 1149.0 14.0 73.2

Recipe 11 β€” Who gets open? NextGen separation πŸ›°οΈβ€‹

The receiving statboard exposes a tracking-only metric box scores can't: average separation at the catch point. Rank qualified targets (β‰₯80) to find the route-runners defenders can't shadow.

sepboard = safe(
"NGS receiving statboard",
lambda: nfl.nfl_ngs_statboard(stat_type="receiving", season=SEASON, season_type="REG"),
)
if sepboard is not None and sepboard.height and "avgSeparation" in sepboard.columns:
sep = (
sepboard.filter(pl.col("targets") >= 80)
.select([c for c in [
"player_displayName", "player_position", "avgSeparation",
"avgYACAboveExpectation", "catchPercentage", "yards",
] if c in sepboard.columns])
.sort("avgSeparation", descending=True)
.head(10)
)
else:
sep = "NGS receiving statboard unavailable"
sep
βœ… NGS receiving statboard





shape: (10, 6)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”
β”‚ player_displayNam ┆ player_position ┆ avgSeparation ┆ avgYACAboveExpec ┆ catchPercentage ┆ yards β”‚
β”‚ e ┆ --- ┆ --- ┆ tation ┆ --- ┆ --- β”‚
β”‚ --- ┆ str ┆ f64 ┆ --- ┆ f64 ┆ i64 β”‚
β”‚ str ┆ ┆ ┆ f64 ┆ ┆ β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•ͺ═════════════════β•ͺ═══════════════β•ͺ══════════════════β•ͺ═════════════════β•ͺ═══════║
β”‚ Khalil Shakir ┆ WR ┆ 4.253744 ┆ 1.420359 ┆ 76.0 ┆ 821 β”‚
β”‚ Demario Douglas ┆ WR ┆ 4.010062 ┆ 0.298947 ┆ 75.862069 ┆ 621 β”‚
β”‚ Zay Flowers ┆ WR ┆ 3.9155 ┆ 1.636763 ┆ 63.793103 ┆ 1059 β”‚
β”‚ Xavier Worthy ┆ WR ┆ 3.773458 ┆ 0.445979 ┆ 60.204082 ┆ 638 β”‚
β”‚ Zach Ertz ┆ TE ┆ 3.631374 ┆ -0.207435 ┆ 72.527473 ┆ 654 β”‚
β”‚ George Kittle ┆ TE ┆ 3.582076 ┆ 2.110873 ┆ 82.978723 ┆ 1106 β”‚
β”‚ Sam LaPorta ┆ TE ┆ 3.529115 ┆ 1.433347 ┆ 72.289157 ┆ 726 β”‚
β”‚ Brock Bowers ┆ TE ┆ 3.515527 ┆ 0.982977 ┆ 73.202614 ┆ 1194 β”‚
β”‚ Trey McBride ┆ TE ┆ 3.511888 ┆ 0.637703 ┆ 75.510204 ┆ 1146 β”‚
β”‚ Jonnu Smith ┆ TE ┆ 3.49632 ┆ 0.848143 ┆ 79.279279 ┆ 884 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”˜

Recipe 12 β€” The nail-biters: closest games of the season πŸ˜¬β€‹

load_nfl_schedule carries the final result (home margin). Take its absolute value and sort ascending to surface the one-score thrillers β€” built-in betting lines ride along too.

sched = nfl.load_nfl_schedule([SEASON])
if {"result", "game_type", "home_team", "away_team"}.issubset(sched.columns):
scored = (
sched.filter((pl.col("game_type") == "REG") & pl.col("result").is_not_null())
.with_columns(pl.col("result").abs().alias("margin"))
)
want = [
"week", "away_team", "away_score", "home_team", "home_score",
"margin", "spread_line", "total_line",
]
nailbiters = (
scored.select([c for c in want if c in scored.columns])
.sort("margin")
.head(10)
)
else:
nailbiters = "schedule schema changed β€” result/team columns missing"
nailbiters
shape: (10, 8)
β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ week ┆ away_team ┆ away_score ┆ home_team ┆ home_score ┆ margin ┆ spread_line ┆ total_line β”‚
β”‚ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- β”‚
β”‚ i32 ┆ str ┆ i32 ┆ str ┆ i32 ┆ i32 ┆ f64 ┆ f64 β”‚
β•žβ•β•β•β•β•β•β•ͺ═══════════β•ͺ════════════β•ͺ═══════════β•ͺ════════════β•ͺ════════β•ͺ═════════════β•ͺ════════════║
β”‚ 2 ┆ CIN ┆ 25 ┆ KC ┆ 26 ┆ 1 ┆ 6.5 ┆ 47.5 β”‚
β”‚ 2 ┆ ATL ┆ 22 ┆ PHI ┆ 21 ┆ 1 ┆ 5.5 ┆ 46.5 β”‚
β”‚ 4 ┆ DEN ┆ 10 ┆ NYJ ┆ 9 ┆ 1 ┆ 7.5 ┆ 39.5 β”‚
β”‚ 5 ┆ ARI ┆ 24 ┆ SF ┆ 23 ┆ 1 ┆ 7.0 ┆ 48.5 β”‚
β”‚ 8 ┆ ARI ┆ 28 ┆ MIA ┆ 27 ┆ 1 ┆ 4.0 ┆ 46.5 β”‚
β”‚ 9 ┆ NO ┆ 22 ┆ CAR ┆ 23 ┆ 1 ┆ -7.0 ┆ 43.5 β”‚
β”‚ 10 ┆ CIN ┆ 34 ┆ BAL ┆ 35 ┆ 1 ┆ 6.0 ┆ 53.0 β”‚
β”‚ 10 ┆ PIT ┆ 28 ┆ WAS ┆ 27 ┆ 1 ┆ 1.5 ┆ 45.0 β”‚
β”‚ 11 ┆ GB ┆ 20 ┆ CHI ┆ 19 ┆ 1 ┆ -6.0 ┆ 41.0 β”‚
β”‚ 11 ┆ IND ┆ 28 ┆ NYJ ┆ 27 ┆ 1 ┆ 4.0 ┆ 43.0 β”‚
β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ—“οΈ Season helpers​

Handy when you want "the current/most-recent season" instead of hard-coding a year. get_current_nfl_season() / get_current_nfl_week() track the live calendar; most_recent_nfl_season() gives the latest season with data.

{
"current_season": nfl.get_current_nfl_season(),
"current_week": nfl.get_current_nfl_week(),
"most_recent_season": nfl.most_recent_nfl_season(),
}

{'current_season': 2025, 'current_week': 22, 'most_recent_season': 2025}

πŸŽ‰ Where to next​

Now go build something great β€” may your EPA be ever positive! πŸ“ˆπŸˆ