data·vault_
← Catalog

NFL data API in Python: requests examples with live endpoints

Guide to the NFL Games & Betting Lines 1999-2026 pack · free 22-row CSV sample · loading files instead? Read the pandas quickstart or the Excel and Google Sheets import guide · every status code and output comment on this page comes from a live run on 2026-09-10.

Two lines of Python ask ESPN's site API for a week of NFL results, no key, no signup:

import requests

r = requests.get(
    "https://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard",
    params={"seasonType": 2, "week": 1, "dates": 2025},
    timeout=30,
)
r.raise_for_status()
events = r.json()["events"]
print(len(events))          # 16
print(events[0]["name"])    # Dallas Cowboys at Philadelphia Eagles

That ran on 2026-09-10 against the real endpoint, output verbatim. A second free route, the nflverse-data releases, serves the entire game history as one CSV that the same requests call can download. The sections below cover both, then the trade-off against a pinned snapshot CSV.

The free routes, checked live

The league itself runs no public API for Python or anything else. What people mean by an NFL data API is one of the routes below or a paid commercial feed. All of these were hit on 2026-09-10:

RouteWhat you getAuthChecked 2026-09-10
ESPN site API, scoreboardOne week of fixtures and results as nested JSONnone200, 16 events
ESPN site API, teamsTeam metadata per club, abbreviation and venuenone200
nflverse-data releases, schedules/games.csvFull 1999-2026 game history, one 2.1 MB CSVnone200, 7,548 rows, updated 2026-09-07
nfl_data_py (PyPI)Wrapper that pulls the same nflverse releases into pandas, import_schedules()noneversion 0.3.3, pins pandas < 2.0
nflreadpy (PyPI)Newer wrapper, load_schedules() into polarsnoneversion 0.1.5

The wrappers are a real option and worth one caveat: nfl_data_py 0.3.3 requires numpy < 2.0 and pandas < 2.0, so it can collide with a current environment. Calling the URLs directly with requests avoids the dependency pinning entirely, which is what the examples here do.

Example 1: ESPN scoreboard JSON

The quick-answer call again, with the score lines pulled out. Competitors sit under each event's competitions list, ESPN returns scores as strings, and the homeAway key tells you which side is which, since the array order is not guaranteed:

import requests

r = requests.get(
    "https://site.api.espn.com/apis/site/v2/sports/football/nfl/scoreboard",
    params={"seasonType": 2, "week": 1, "dates": 2025},
    timeout=30,
)
r.raise_for_status()
data = r.json()
for e in data["events"][:2]:
    comp = e["competitions"][0]["competitors"]
    home = next(c for c in comp if c["homeAway"] == "home")
    away = next(c for c in comp if c["homeAway"] == "away")
    print(f'{away["team"]["abbreviation"]} {away["score"]} at '
          f'{home["team"]["abbreviation"]} {home["score"]}')
# DAL 20 at PHI 24
# KC 21 at LAC 27

Week 1 of the 2025 season returned 16 events, and the two games above closed 24-20 and 27-21. Swap params for {"seasonType": 2, "week": 1, "dates": 2026} to see the current season's opening week fill in as results land. Team lookups follow the same pattern at /teams/kc, also 200 on the checked date.

One honest limit: this is an undocumented site endpoint, not a published product. The response shape held long enough for the example above, but treat the deep nesting as a display feed, read the fields you need, and do not build a long-lived pipeline on assumptions about the structure.

Example 2: the nflverse games.csv over HTTP

Where ESPN covers a week, the nflverse-data releases cover every season since 1999 in one file. The download URL redirects to a CDN, and requests follows it automatically:

import csv, io, requests

url = "https://github.com/nflverse/nflverse-data/releases/download/schedules/games.csv"
r = requests.get(url, timeout=60)
r.raise_for_status()
print(r.status_code, len(r.content))    # 200 2177170

rows = list(csv.reader(io.StringIO(r.text)))
print(len(rows) - 1)                    # 7548

7,548 game rows, the same count the preprocessed pack carries. Before you parse, you can ask the release how fresh it is:

stamp = requests.get(
    "https://github.com/nflverse/nflverse-data/releases/download/schedules/timestamp.json",
    timeout=30,
).json()
print(stamp["last_updated"])    # 2026-09-07 04:16:18 EDT

To hand the file to pandas, pd.read_csv(io.StringIO(r.text)) works on the same response object, and the columns match the ones in the pandas quickstart, which loads this site's 22-row sample with the same schema.

Live API or snapshot CSV? The honest trade-off

Both routes get you the same game rows, and the right pick depends on how often you rerun and what has to stay true between runs:

Live API or release filePinned snapshot CSV
FreshnessCurrent week the moment it lands; release file refreshed in season (2026-09-07 at the last check)Static: results through the 2025 season plus scheduled 2026 rows, refresh means re-download
Cost per runScoreboard JSON is a few KB; full history is 2.1 MB every single callDownload once, everything after is local disk
StabilityESPN is undocumented and can shift shape; the nflverse file follows its source schemaSchema pinned, SHA-256 checksums in the pack, joins do not break between runs
Work done for youRaw fields: you normalize spreads and derive records yourselfNormalized spread and moneyline columns, 861 team records, 27 season summaries, data dictionary
Best forDashboards, current-week scripts, one-off lookupsRepeat analysis, offline work, agents and notebooks that need reproducible inputs

The practical answer for most projects is both, in one script: pull the scoreboard for this week's games, load the snapshot for everything before this season. Neither makes the other wrong; they cover different halves of the freshness-versus-stability line.

From live calls to a pinned dataset

The free 22-row sample on this site is the first 22 rows of that same games.csv lineage, and the full pack ships the whole table preprocessed with the derived tables, the column guide, and SHA-256 checksums so the snapshot can be verified like the release file can. For a whole page on the ESPN side of that pairing, the ESPN API in Python guide covers the scoreboard and team endpoints. Raw nflverse route vs preprocessed pack, laid out in detail: the nflverse comparison guide.

Checkout and download run through Getly. The full pack is 3 CSVs (7,548 + 861 + 27 rows), data dictionary, source SHA-256 checksums. The sample downloads free here, no signup.

NFL data API questions

Is there a free NFL API for Python?
Yes, two of them work without keys. ESPN's site API returns a week of scores and fixtures as JSON, and the nflverse-data releases publish the full games.csv that a plain requests call can download. The league itself runs no official public API; the paid commercial feeds are separate products. Both free routes were checked live on 2026-09-10 and returned 200.
Do I need an API key for the ESPN scoreboard or the nflverse files?
No for either. A requests.get call with no auth headers returned 200 for both on 2026-09-10. The difference is the contract: ESPN's endpoint is undocumented, so its response shape can change without notice, while the nflverse file is a full 2.1 MB download on every call, with the update time recorded in its timestamp.json.
Should my script call a live API or load a snapshot CSV?
Use the live route for the current week's scoreboard and the snapshot for any analysis you rerun over days or weeks. The nflverse file costs a 2.1 MB download per run and the ESPN shape can shift, while a snapshot CSV keeps a pinned schema, ships with SHA-256 checksums and derived tables like team records and season summaries, and works offline. Many scripts use both: live results on top of a pinned history.

Sources and license

All game rows trace back to the nflverse-data project, file schedules/games.csv, released under CC BY 4.0; credit it as "Data from nflverse, CC BY 4.0". Samples on this site carry the same license. The ESPN endpoint is a public site API with no documentation contract behind it. No logos or league marks appear anywhere in the pack; team names are factual data. This page and the pack describe data only and offer no betting advice, picks, or predictions.