ESPN Player Stats Scraper - NFL, NBA, MLB, NHL Box Scores avatar

ESPN Player Stats Scraper - NFL, NBA, MLB, NHL Box Scores

Pricing

from $0.90 / 1,000 saved player rows

Go to Apify Store
ESPN Player Stats Scraper - NFL, NBA, MLB, NHL Box Scores

ESPN Player Stats Scraper - NFL, NBA, MLB, NHL Box Scores

Scrape ESPN player stats, box scores and game logs for the NFL, NBA, MLB, NHL, WNBA, NCAA and soccer. One row per player per game, with every stat, team, opponent and score. Filter by date, team, player or position.

Pricing

from $0.90 / 1,000 saved player rows

Rating

0.0

(0)

Developer

Harish Garg

Harish Garg

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

3

Monthly active users

11 days ago

Last modified

Categories

Share

ESPN Player Stats Scraper — NFL, NBA, MLB, NHL, WNBA, College & Soccer Box Scores

ESPN Player Stats Scraper gets per-player box scores and game logs from ESPN for the NFL, NBA, MLB, NHL, WNBA, NCAA football and basketball, MLS, the Premier League, La Liga and the UEFA Champions League. You get one clean row per player per game, with the player, the position, the team, the opponent, the score, and every stat ESPN publishes for that game.

Pick the leagues and the days, or paste ESPN game links, and download the results as JSON, CSV, Excel, XML or HTML. You can also read them through the Apify API, send them to Google Sheets, Zapier or Make, or let an AI assistant such as Claude or ChatGPT run the scraper for you.

No ESPN account. No API key. No coding needed.

What does ESPN Player Stats Scraper do?

It turns ESPN box scores into a player-level dataset. For each game in your date range, it returns:

  • Player — name, ESPN player ID, jersey number, position, headshot, starter flag, and did-not-play reason
  • Team and opponent — name, abbreviation, home or away, final score, and winner
  • Game — ESPN game ID, date and time, season, week, status, venue, and attendance
  • Stats — every stat in the ESPN box score, grouped by category (for example passing, rushing, batting, pitching, goalies)

A player who appears in more than one stat group, such as a quarterback who also runs, gets one row that holds all of the player's stat groups.

Supported leagues

SportLeagueCode
American footballNFLnfl
American footballNCAA Footballncaaf
BasketballNBAnba
BasketballWNBAwnba
BasketballNCAA Men's Basketballncaam
BasketballNCAA Women's Basketballncaaw
BaseballMLBmlb
Ice hockeyNHLnhl
SoccerMLSmls
SoccerEnglish Premier Leagueepl
SoccerLa Ligalaliga
SoccerUEFA Champions Leagueucl

Why scrape ESPN player stats?

  • Fantasy sports — Get the full player slate after every game night. Build projections from real game logs, not season averages.
  • Sports betting models — Get the player-level history that player-prop models need. Filter to one team, one player, one position, or one stat group.
  • Player research and scouting — Compare players across games, opponents and venues over a date range.
  • Dashboards, bots and newsletters — Schedule a run after the games end to feed a leaderboard, a Slack or Discord digest, or a weekly recap.
  • Sports analytics and machine learning — Build season-long game-log tables for analysis, one month at a time.

How to get ESPN player stats in Apify Console

  1. Open ESPN Player Stats Scraper in Apify Console and click Try for free.
  2. In Leagues, select one or more leagues.
  3. Set Start Date and End Date. Pick a calendar date, or type a relative value such as 7 days. The prefilled range is the last 7 days.
  4. Optional: add a Team, Player Name, Position or Stat Group filter.
  5. Click Save & Start.
  6. When the run finishes, open the Output tab. The Player stats view shows each player's stat line, and the Game details view shows the game, the score and the venue. Download the data as JSON, CSV or Excel, or copy the API link.

To get new stats every day, create a Schedule for the run. To send the data to another app, add an Integration or a Webhook.

Input

FieldTypeDefaultDescription
leaguesarray["nfl"]League codes from the table above. Use exactly one league when you set eventIds.
startDatestring7 daysFirst day. YYYY-MM-DD, or a relative value such as 7 days or 2 weeks.
endDatestringtodayLast day. YYYY-MM-DD or a relative value. Empty means today, including live games.
gameStatusstringallall (final and live games), post (final games only), or in (live games only).
teamsarray[]Team abbreviations (KC, LAL, NYY, LIV) or parts of team names (Chiefs, Man City, Atletico).
playersarray[]Player name terms. Partial match. Case and accents do not matter, so mbappe finds Kylian Mbappé.
positionsarray[]Position abbreviations (QB, WR, PG, C, SP, G).
statGroupsarray[]Stat groups to keep (passing, batting, goalies).
includeDidNotPlaybooleanfalseInclude bench players with no stat line.
eventIdsarray[]ESPN game IDs or game page links to get directly. Start and end dates are then ignored.
maxGamesinteger100The most games one run reads (maximum 500). When more games match, the most recent ones are kept.

Dates

Relative dates count back from today, in US Eastern time, to match ESPN's game days. Most leagues do not play every day, so a range of a week finds games in any league that is in season.

You wantstartDateendDate
Today and the 7 days before it7 daysempty
Last week's final games7 days1 day
Yesterday1 day1 day
Today, including live games0 daysempty
One date2026-09-272026-09-27
A fixed range2026-09-012026-09-15

One run covers up to 31 days. For a longer range, the run keeps the most recent 31 days. An end date in the future changes to today.

Get a single game

Open the game on espn.com and copy the link from the address bar. Paste the whole link into Game IDs, or only the number after /gameId/. For example, https://www.espn.com/nfl/game/_/gameId/401872953/chargers-bills and 401872953 give the same game.

Input examples

Last week's NFL player stats:

{ "leagues": ["nfl"], "startDate": "7 days", "endDate": "1 day" }

Every quarterback's passing line for today and the 7 days before it:

{ "leagues": ["nfl"], "startDate": "7 days", "positions": ["QB"], "statGroups": ["passing"] }

One NBA player's game log for a month. A player filter searches every game in the range, so set maxGames high enough for all of them:

{ "leagues": ["nba"], "startDate": "2026-03-01", "endDate": "2026-03-31", "players": ["doncic"], "maxGames": 500 }

One MLB team for the last 2 weeks:

{ "leagues": ["mlb"], "startDate": "2 weeks", "teams": ["Yankees"] }

Every goalkeeper in the Premier League and MLS for the last 14 days:

{ "leagues": ["epl", "mls"], "startDate": "14 days", "positions": ["G"] }

A single game from its ESPN link:

{ "leagues": ["nfl"], "eventIds": ["https://www.espn.com/nfl/game/_/gameId/401872953/chargers-bills"] }

Output

Each dataset item is one player in one game. Rows come in game order, oldest game first.

{
"gameId": "401872953",
"league": "nfl",
"leagueName": "NFL",
"sport": "football",
"date": "2026-09-27T17:00Z",
"dateLocal": "2026-09-27T13:00:00.000-04:00",
"season": { "year": 2026, "type": 2 },
"week": 3,
"status": { "state": "post", "description": "Final", "completed": true },
"venue": { "name": "Highmark Stadium", "city": "Orchard Park", "state": "NY" },
"attendance": 60361,
"neutralSite": false,
"team": { "id": "2", "name": "Buffalo Bills", "abbreviation": "BUF", "homeAway": "home", "score": 24, "winner": true },
"opponent": { "id": "24", "name": "Los Angeles Chargers", "abbreviation": "LAC", "score": 16 },
"player": {
"id": "3918298",
"name": "Josh Allen",
"shortName": null,
"jersey": "17",
"position": "Quarterback",
"positionAbbreviation": "QB",
"headshot": "https://a.espncdn.com/i/headshots/nfl/players/full/3918298.png",
"starter": null,
"didNotPlay": false,
"dnpReason": null,
"ejected": null
},
"statGroups": ["passing", "rushing", "receiving", "fumbles"],
"stats": {
"passing": {
"completions/passingAttempts": "16/26",
"passingYards": 204,
"yardsPerPassAttempt": 7.8,
"passingTouchdowns": 0,
"interceptions": 2,
"sacks-sackYardsLost": "4-30",
"adjQBR": 50.8,
"QBRating": 54
},
"rushing": {
"rushingAttempts": 8,
"rushingYards": 22,
"yardsPerRushAttempt": 2.8,
"rushingTouchdowns": 2,
"longRushing": 8
},
"receiving": {
"receptions": 1,
"receivingYards": 1,
"yardsPerReception": 1,
"receivingTouchdowns": 0,
"longReception": 1,
"receivingTargets": 1
},
"fumbles": { "fumbles": 1, "fumblesLost": 1, "fumblesRecovered": 0 }
}
}

Output fields

FieldDescription
gameIdESPN game ID
league, leagueName, sportLeague code, league name, and sport
dateGame start time in UTC
dateLocalGame start time in US Eastern time
season.year, season.typeSeason year, and season type (1 = preseason, 2 = regular season, 3 = postseason)
weekWeek number (football)
statusstate is post (final) or in (live), with ESPN's status text
venue, attendance, neutralSiteWhere the game was played, and the crowd size
teamThe player's team: ID, name, abbreviation, home or away, score, winner
opponentThe opposing team: ID, name, abbreviation, score
playerID, name, jersey, position, headshot, starter, did-not-play flag and reason, ejected
player.subbedIn, player.subbedOut, player.formationPlaceSoccer only: substitutions and formation slot
statGroupsThe stat groups in this row
statsStats by group, then by ESPN stat name

Stat groups by sport

SportStat groups
American footballpassing, rushing, receiving, fumbles, defensive, interceptions, kickReturns, puntReturns, kicking, punting
Baseballbatting, pitching
Ice hockeyforwards, defenses, goalies
Basketballstats
Soccerstats

Stat values

  • Plain numbers are numbers (204, 0.252, -2), so you can sum, sort and average them.
  • Combined values stay text, because they hold two numbers: "16/26" (completions out of attempts), "4-30" (sacks and yards lost), "24:31" (time on ice).
  • A stat that ESPN leaves blank is null, not 0.
  • Baseball innings pitched use the baseball convention: 1.2 means one and two-thirds innings. Do not add it as a normal decimal.

Use the ESPN Player Stats Scraper with the Apify API

You can start the scraper and read its results from any language through the Apify API. Get your API token in Apify Console under Settings → API & Integrations, and replace <YOUR_API_TOKEN> below.

HTTP

Run the scraper and get the results in one call:

curl -X POST "https://api.apify.com/v2/acts/hgservices~espn-player-stats-scraper/run-sync-get-dataset-items?format=json" \
-H "Authorization: Bearer <YOUR_API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{ "leagues": ["mlb"], "startDate": "1 day", "endDate": "1 day", "teams": ["NYY"] }'

Change format=json to format=csv or format=xlsx to get a spreadsheet. This endpoint waits up to 300 seconds. For a long date range, start the run, and then read the dataset when the run finishes:

# 1. Start the run. The answer contains data.id (the run ID) and data.defaultDatasetId.
curl -X POST "https://api.apify.com/v2/acts/hgservices~espn-player-stats-scraper/runs" \
-H "Authorization: Bearer <YOUR_API_TOKEN>" \
-H "Content-Type: application/json" \
-d '{ "leagues": ["nba", "wnba"], "startDate": "30 days", "maxGames": 500 }'
# 2. When the run status is SUCCEEDED, get the rows.
curl "https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=json" \
-H "Authorization: Bearer <YOUR_API_TOKEN>"

JavaScript / Node.js

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: '<YOUR_API_TOKEN>' });
const run = await client.actor('hgservices/espn-player-stats-scraper').call({
leagues: ['nba'],
startDate: '1 day',
endDate: '1 day',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const row of items) {
console.log(row.player.name, row.team.abbreviation, row.stats.stats?.points);
}

Python

Use apify-client 3.0 or later.

from apify_client import ApifyClient
client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("hgservices/espn-player-stats-scraper").call(run_input={
"leagues": ["nfl"],
"startDate": "7 days",
"positions": ["QB"],
})
if run is None:
raise RuntimeError("The run was not found.")
for row in client.dataset(run.default_dataset_id).iterate_items():
print(row["player"]["name"], row["stats"].get("passing"))

Use ESPN player stats in Claude, ChatGPT and other AI assistants

This scraper works as a tool for AI assistants through the Apify MCP server. Your assistant can find the scraper, read its input options, run it, and use the player stats in its answer.

Claude (claude.ai or Claude Desktop) — Go to Settings → Connectors, add a custom connector, and enter https://mcp.apify.com. Sign in to Apify when Claude asks.

Claude Code

$claude mcp add --transport http apify https://mcp.apify.com

ChatGPT — Turn on Developer mode in Settings → Apps & Connectors → Advanced settings. Then create a connector with the MCP server URL https://mcp.apify.com.

Cursor, VS Code and other MCP clients — Add https://mcp.apify.com as a remote MCP server. To give the assistant only this scraper, use https://mcp.apify.com?tools=hgservices/espn-player-stats-scraper.

Then ask in plain language:

Use the ESPN Player Stats Scraper to get last week's NFL quarterback stats, then rank the quarterbacks by passing yards.

Get Luka Doncic's NBA game log for March 2026 and list the points, rebounds and assists for each game.

Which Premier League goalkeepers made the most saves in the last 14 days?

For the best results, tell the assistant the league, the dates, and any team or player you want. This keeps the run fast.

Integrations

Connect the scraper to the tools you already use with Apify integrations: Google Sheets, Slack, Zapier, Make, n8n, Airbyte, Keboola, GitHub and more. You can also use webhooks to call your own endpoint when a run finishes.

Schedule examples

Use caseCronInput
Nightly game logs0 6 * * *startDate and endDate both 1 day
NFL week recap0 8 * * 2leagues: ["nfl"], startDate: "7 days", endDate: "1 day"
Live player tracking*/10 * * * *startDate: "0 days", gameStatus: "in"

Run summary

Each run also saves a SUMMARY record in the key-value store, and the Output tab links to it. It shows the number of player rows, the number of games found and read, the dates that the run used, the filters, and notes that explain an empty result. A shortened example:

{
"totalPlayerRows": 1041,
"gamesFound": 16,
"gamesFetched": 16,
"gamesSkippedByMaxGames": 0,
"gamesWithoutBoxscore": 0,
"leagues": ["nfl"],
"dateRange": { "startDate": "2026-09-26", "endDate": "2026-10-02" },
"filters": { "teams": [], "players": [], "positions": [], "statGroups": [], "gameStatus": "all", "includeDidNotPlay": false },
"notes": []
}

Tips for fast runs

  • Use a team filter. The scraper then reads only the games that the team played.
  • Set Maximum Games to limit a large run. If a run finds more games than this limit, it keeps the most recent ones, and the log and the SUMMARY record tell you how many games it skipped.
  • Use a short date range for scheduled runs, such as 1 day.

Data notes

  • NFL and NCAA Football positions come from the current team roster, because ESPN's football box scores do not list them. A player who has left the team since the game has no position. Football box scores also have no starter flag.
  • MLB box scores have no jersey number.
  • Soccer substitutes have the position SUB.
  • Postponed and cancelled games have no box score, so they give no rows.
  • Live games give partial stats that change until the game ends.
  • Scheduled games have no player stats, so the scraper never includes them.

FAQ

Do I need an ESPN account or an API key? No. You need only an Apify account.

How far back can I get player stats? ESPN has box scores for many past seasons. For example, NBA games from 2015 work. One run covers up to 31 days, so run the scraper once per month to build a full season.

Why did my run return no rows? Usually the league had no games on those days (an off day, a break or the off-season), or the filters matched nothing. The status message, the run log and the SUMMARY record tell you which one, and what to change.

Can I get stats for a live game? Yes. Set startDate to 0 days, leave endDate empty, and set gameStatus to in.

Can I search for names with accents? Yes. Accents do not matter in the player and team filters. Mbappe and Mbappé give the same result, and so do Atletico and Atlético.

Can I combine this data with game scores and schedules? Yes. The ESPN Sports Scores & Schedules scraper gives one row per game. The league, gameId, team and opponent fields are the same in both datasets, so you can join them.

Is it legal to scrape ESPN? This scraper collects only public sports statistics and never signs in. You are responsible for how you use the data, and for ESPN's terms of use.

Support

Did you find a bug, or do you need a field or a league that is not here? Open an issue on the Issues tab. Custom solutions are available on request.

This scraper uses public data from ESPN. It is not affiliated with, endorsed by, or sponsored by ESPN.