data·vault_
← Catalog

NFL stats by team CSV: 861 team-season rows, 1999-2025

Guide to team-records-by-season.csv in the NFL Games & Betting Lines 1999-2026 pack · snapshot 2026-09-06 · for the game-level table, read the games.csv column guide.

The NFL stats-by-team file in this catalog is team-records-by-season.csv: 861 rows, one per team per season from 1999 through 2025, playoffs included. Each row carries 15 columns: games played, wins, losses, ties, points for and against, the against-the-spread record with pushes broken out, and the over/under record. All of it was computed from the 7,548-row games table by a script that ships with the pack, so you can audit or rebuild every number.

You can work with the table three ways: query it free over MCP (100 rows per call, no signup), compute your own per-team splits from the free games sample, or download the full 861-row CSV with the full pack. The sections below show the schema, real sample rows, and the queries.

The 15 columns

ColumnMeaning
seasonNFL season year, 1999 to 2025
team3-letter nflverse abbreviation, e.g. KC, PHI, DAL
gamesGames played that season, regular season plus playoffs
wins, losses, tiesW-L-T record, playoff games included
points_forTotal points scored
points_againstTotal points allowed
ats_winsGames where the team covered the closing spread
ats_lossesGames where the team failed to cover
ats_pushesGames that landed exactly on the spread (integer lines only)
ats_win_pctats_wins / (ats_wins + ats_losses), pushes excluded
oversGames whose combined score went over total_line
undersGames that went under
total_pushesGames whose combined score equaled total_line exactly

ATS stands for against the spread. A team covers when its margin of victory beats the line it gave, computed as margin + team_spread > 0. A push lands exactly on the line, which only happens on integer spreads like -3; half-point lines like 3.5 cannot push. The same three-way split applies to totals. Pushes count as neither a cover nor a miss, and ats_win_pct excludes them.

Sample rows from the file

Ten of the 861 rows, copied verbatim from team-records-by-season.csv. Read a row like 2024,KC as: 20 games, a 17-3 record, 462 points scored, 409 allowed.

seasonteamgameswinslossestiespoints_forpoints_againstats_winsats_lossesats_pushesats_win_pctoversunderstotal_pushes
1999ARI16610024538251100.312790
1999BAL168803242778800.5790
1999DAL1789036230311510.6887100
2024KC20173046240917300.859110
2024DET18153059538716200.8891071
2024TEN17314031146021500.1181070
2025KC17611036232811600.6474130
2025PHI18117039834811610.6477110
2025BUF19136053842215400.7891090
2025ARI17314035548841300.2351070

A few rows here mark things to know before you aggregate. KC 2024 shows 20 games because playoffs count: 17-3 is the 15-2 regular season plus a 2-1 playoff run. The first three seasons carry 31 teams each (the Texans joined in 2002), which is why 27 seasons produce 861 rows instead of 864. And franchise codes stay at their old homes: OAK, SD, STL, plus WAS through 2021 and WSH from 2022. Map those before joining across decades.

Query examples: filter by team and season

Free over MCP, no download

The dataset-mcp server queries the released 861-row file directly. Start the server, then call query_dataset. Every Chiefs season since 2020, first page:

npx --allow-git=all -y github:jayjex/dataset-mcp
# plain form works on npm 10/11: npx -y github:jayjex/dataset-mcp

query_dataset("nfl-games", {file: "nfl-team-records-by-season.csv", where: [{column: "team", op: "=", value: "KC"}, {column: "season", op: "gt", value: 2019}], limit: 10})

total_matched: 6 | next_offset: null
2020,KC,19,16,3,0,542,434,17,2,0,0.895,9,10,0
2021,KC,20,14,6,0,588,448,18,2,0,0.9,12,8,0
2022,KC,20,17,3,0,584,444,18,2,0,0.9,9,11,0
...

Filters combine, operators are =, contains, gt, and lt, and get_stats returns counts, min/max, and top-N values for any column without paging through rows. Setup details live on the for agents page. This runs against the full file and costs nothing.

Team splits at the game level work the same way on the main games table. Every 2024 Chiefs game, home or away, free:

query_dataset("nfl-games", {where: [{column: "season", op: "=", value: 2024}, {column: "home_team", op: "=", value: "KC"}], limit: 5})
total_matched: 10 | next_offset: 5

query_dataset("nfl-games", {where: [{column: "season", op: "=", value: 2024}, {column: "away_team", op: "=", value: "KC"}], limit: 5})
total_matched: 10 | next_offset: 5

Ten home games, ten away games, 20 total: the playoff games count too. Compare those call outputs against the single KC 2024 row in the team table and the whole derivation checks out in a browser tab. For the live side of the same abbreviations, the ESPN API in Python guide pulls all 32 club codes from ESPN's teams endpoint and shows how they join back to this file.

In pandas, on the full CSV

After loading the table from the pack:

import pandas as pd
df = pd.read_csv("team-records-by-season.csv")

kc = df[(df.team == "KC") & (df.season >= 2015)]

# point differential per team-season
df["point_diff"] = df.points_for - df.points_against
top_2025 = df[df.season == 2025].sort_values("point_diff", ascending=False).head(10)

# best single-season cover rates since 2010
best_ats = df[df.season >= 2010].nlargest(10, "ats_win_pct")[
    ["season", "team", "wins", "losses", "ats_wins", "ats_losses", "ats_win_pct"]]

In Excel or Google Sheets

Open the CSV, click any cell, Data → Create a filter. Set team = KC to get the 27 season rows, or season = 2025 to get all 32 teams from one season. Sort by points_against ascending for best defenses, or by ats_win_pct for the year's biggest overperformers against closing lines.

How to download the 861 rows

FreeFull pack
team-records-by-season.csvqueryable over MCP, 100 rows/callcomplete 861-row CSV download
season-summaries.csvall 27 rows, free downloadsame table
games.csv22-row sampleall 7,548 rows, 46 columns
derive script + dictionarydescribed in this guidederive.py included, full dictionary

Checkout and download run through Getly. The full pack is 3 CSVs (7,548 + 861 + 27 rows), the derivation script, a data dictionary, and source SHA-256 checksums.

Source and license

Every number in the table was computed from nflverse's schedules/games.csv, downloaded 2026-09-06 with the SHA-256 recorded in the pack. nflverse-data is released under CC BY 4.0, and the derived table carries the same terms. If you publish work built on this data, credit it like this: "Data from nflverse, CC BY 4.0". Team names are factual data; no logos or league marks appear in the pack.

What this data is for

The table is historical record: per-team-per-season results and line outcomes as they stood at each game. Common uses are research and analysis, such as ranking point differentials across eras, comparing franchise scoring trends, or splitting a model's training data by team-season. This page and the pack describe the data only and offer no betting advice, picks, or predictions. To roll the game rows up by league side instead of by franchise, the NFL data by conference guide maps every abbreviation to AFC or NFC and runs the head-to-head on the real sample.

FAQ

Where can I get NFL stats by team as a CSV?

Data Vault's NFL pack includes team-records-by-season.csv: 861 rows with games, wins, losses, ties, points for and against, ATS record, and over/under record for every team and season from 1999 through 2025. The whole table is queryable free over MCP (100 rows per call, no signup), and the complete CSV downloads with the pack from Getly.

What seasons and teams does the 861-row table cover?

Seasons 1999 through 2025, playoffs included, so a Super Bowl team shows 20-21 games that season. The first three seasons carry 31 teams each and the rest carry 32 after the Texans joined in 2002, which is why the table has 861 rows instead of 864.

Can I query team-season stats without downloading anything?

Yes. The dataset-mcp server queries the released 861-row file directly: run npx -y github:jayjex/dataset-mcp, then filter on any column. A call like query_dataset("nfl-games", {file: "nfl-team-records-by-season.csv", where: [{column: "team", op: "=", value: "KC"}], limit: 10}) returns the Chiefs' season rows. No API key, no cost, 100 rows per call.

What do ats_wins and overs mean in the file?

ats_wins counts games where the team covered the closing spread, ats_losses counts failures, and ats_pushes counts games that landed exactly on an integer line; ats_win_pct divides wins by wins plus losses, so pushes are excluded. overs, unders, and total_pushes apply the same three-way split to the closing total line. These are historical data columns for research, not predictions or betting advice.