Sports Reference Stats Scraper - NBA & NHL avatar

Sports Reference Stats Scraper - NBA & NHL

Pricing

from $21.00 / 1,000 player season records

Go to Apify Store
Sports Reference Stats Scraper - NBA & NHL

Sports Reference Stats Scraper - NBA & NHL

Scrape NBA player and team stats from Basketball-Reference and NHL stats from Hockey-Reference: per-season stats, advanced metrics (PER, win shares, BPM), team rosters and salaries. One row per season, 23 seasons for LeBron James in one page. Export CSV, Excel, JSON. No login or API key.

Pricing

from $21.00 / 1,000 player season records

Rating

0.0

(0)

Developer

RecordsData

RecordsData

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

18 hours ago

Last modified

Share

PunkRecordsData

Sports Reference Stats Scraper scrapes player and team pages from Basketball-Reference (NBA) and Hockey-Reference (NHL). Paste a player or team URL, get one row per season with every stat column the page publishes, plus optional advanced metrics, rosters and salaries. Export CSV, Excel, JSON or API. No login, no API key. Verified 2026-10-03. From $25 per 1,000 season rows.

Sports Reference Stats Scraper turns any Basketball-Reference or Hockey-Reference player or team-season page into structured data. A single LeBron James page returned 23 season rows with 36 columns per row in our test run, and a single Boston Celtics 2024 team page returned 19 roster rows, 19 advanced-stats rows and 19 per-game rows. It is built for sports analysts, fantasy players, bettors, journalists and data scientists who need historical NBA and NHL numbers without copy-pasting tables.

๐Ÿ€ What does Sports Reference Stats Scraper do?

  • Scrapes NBA player career stats from Basketball-Reference, one row per season.
  • Scrapes NHL player career stats from Hockey-Reference, one row per season.
  • Scrapes team-season pages (for example the 2024 Boston Celtics) with per-game stats for every player.
  • Adds advanced metrics on request: PER, win shares, BPM and VORP for basketball, possession and rate stats for hockey.
  • Adds the full team roster on request: number, position, height, weight, birth date, college.
  • Adds NBA salary history per season on request, including the tables the sites hide inside HTML comments.

Every row is tagged with sport and pageType, and the column names are the ones the source site uses (pts_per_g, ws_per_48, goals, and so on), so numbers match what you see on the page.

๐Ÿ“Š What data can you extract from Basketball-Reference and Hockey-Reference?

FieldDescription
sportbasketball or hockey
pageTypeplayer or team
namePlayer or team name
urlSource page URL
year_idSeason, for example 2012-13 (player pages)
age, team_name_abbr, comp_name_abbr, posAge, team, league and position for that season
games, games_started, mp_per_gGames played, started and minutes per game
pts_per_g, trb_per_g, ast_per_g, stl_per_g, blk_per_gBasketball per-game production
fg_pct, fg3_pct, ft_pct, efg_pctShooting percentages (basketball)
goals, assists, and other hockey columnsHockey season stats, as published by Hockey-Reference
awardsSeason awards and honors when the player has any, for example MVP-1,AS,NBA1
per, ws, bpm, vorpAdvanced metrics (basketball, with Advanced stats on)
player, height, weight, birth_date, collegeRoster fields (team pages, with Team roster on)
season, team_name, salarySalary rows (basketball, with Salaries on)
scrapedAtISO timestamp of the scrape (UTC)

Cells the source leaves empty are left out of the row instead of being filled with a placeholder, so rows never contain empty columns. In our 10-row LeBron James test there were 36 columns and none of them was empty across all rows.

๐Ÿงพ Sample output of the Sports Reference Stats Scraper

Real row from a cloud run on basketball-reference.com/players/j/jamesle01.html (LeBron James, season 2012-13):

{
"sport": "basketball",
"pageType": "player",
"name": "LeBron James",
"url": "https://www.basketball-reference.com/players/j/jamesle01.html",
"year_id": "2012-13",
"age": "28",
"team_name_abbr": "MIA",
"comp_name_abbr": "NBA",
"pos": "PF",
"games": "76",
"games_started": "76",
"mp_per_g": "37.9",
"fg_per_g": "10.1",
"fga_per_g": "17.8",
"fg_pct": ".565",
"fg3_per_g": "1.4",
"fg3a_per_g": "3.3",
"fg3_pct": ".406",
"fg2_per_g": "8.7",
"fg2a_per_g": "14.5",
"fg2_pct": ".602",
"efg_pct": ".603",
"ft_per_g": "5.3",
"fta_per_g": "7.0",
"ft_pct": ".753",
"orb_per_g": "1.3",
"drb_per_g": "6.8",
"trb_per_g": "8.0",
"ast_per_g": "7.3",
"stl_per_g": "1.7",
"blk_per_g": "0.9",
"tov_per_g": "3.0",
"pf_per_g": "1.4",
"pts_per_g": "26.8",
"awards": "MVP-1,DPOY-2,AS,NBA1,DEF1",
"scrapedAt": "2026-10-04T04:50:40.409Z"
}

Values are strings exactly as the site prints them (for example .565), so you can cast them in your own pipeline.

๐Ÿ’ฐ How much does it cost to scrape Basketball-Reference and Hockey-Reference?

The Actor uses pay-per-event pricing. You pay only for rows that are saved to the dataset. Prices below are the live Free-plan prices; paid Apify plans get lower per-event prices (down to $21 per 1,000 season rows on the highest plan).

EventWhat you are charged forPrice per 1,000
player-season-recordOne season row of per-game stats (the base event)$25.00
player-advanced-recordOne advanced-metrics row (optional)$12.00
team-roster-recordOne roster row on a team page (optional)$10.00
salary-recordOne salary row, basketball only (optional)$12.00

You are not charged for error rows, pages that return 404, empty tables or duplicate rows. A run that cannot deliver any data fails with a clear message and charges nothing.

Real examples from our test runs, at the Free-plan price:

  • 10 season rows of one player: 10 events, about $0.25.
  • One team-season page with Advanced stats and Team roster on, 3 season rows: 3 + 19 + 19 events, about $0.49.
  • One player page, salaries only (1 season row + 23 salary rows): about $0.30.

Set a maximum cost per run in the Apify run options and the Actor stops cleanly when it is reached, with the status message "Stopped at your max charge limit". Free Apify users are limited to 10 season rows per run.

๐Ÿš€ How to scrape Basketball-Reference and Hockey-Reference (3 steps)

  1. Open the Actor in Apify Console and paste one or more player or team URLs into Start URLs, for example https://www.basketball-reference.com/players/j/jamesle01.html or https://www.hockey-reference.com/players/o/ovechal01.html.
  2. Set Max Items (season rows to return) and switch on Advanced stats, Team roster or Salaries only if you need them.
  3. Click Start, then download the dataset as JSON, CSV, Excel or XML from the Output tab.

โš™๏ธ Input of the Sports Reference Stats Scraper

FieldTypeDescription
startUrlsarrayPlayer or team-season pages on basketball-reference.com or hockey-reference.com. Default: LeBron James.
maxItemsintegerMaximum season rows to return. Free users: 10. Paid users: up to 1,000,000. Extra rows from the options below are not counted here.
includeAdvancedbooleanAdd advanced metrics rows (default false).
includeRosterbooleanAdd the team roster, team URLs only (default false).
includeSalariesbooleanAdd salary rows, basketball only (default false).
{
"startUrls": [
{ "url": "https://www.basketball-reference.com/players/j/jamesle01.html" },
{ "url": "https://www.basketball-reference.com/teams/BOS/2024.html" }
],
"maxItems": 50,
"includeAdvanced": true,
"includeRoster": true,
"includeSalaries": false
}

๐Ÿ“ฆ Output of the Sports Reference Stats Scraper

Results go to the default dataset. The Player Seasons view shows the key columns, and the full dataset has every column the source table publishes. Error rows (for example an unsupported URL) carry only an error field and are never billed. If a page genuinely has no data, the run ends as succeeded with the status message "No rows found" and zero charge. If every URL fails because of a block, a bad URL or a layout change, the run fails with a clear message.

โš–๏ธ Basketball-Reference scraper vs other Apify actors

Prices below are the public Free-plan prices read from the Apify API on 2026-10-03.

ActorSportsEventsPrice per 1,000 rows
Sports Reference Stats Scraper (this one)NBA and NHL in one Actor4 (season, advanced, roster, salary)$25 season row, $10 to $12 for extras
parseforge basketball-reference-scraper and hockey-reference-scraperOne sport each1 flat event$21
lulzasaur basketball-reference-scraper and hockey-reference-scraperOne sport each1 flat event$10

We are more expensive on the base row than both alternatives. What the extra buys: NBA and NHL in a single input list, optional advanced metrics, roster and salary rows as separate switches (you pay for them only when you turn them on), and the salary and advanced tables that the sites ship inside HTML comments. If you only need a flat table of one sport and price is the main factor, a cheaper single-sport actor may fit you better.

๐Ÿ’ก Use cases for Basketball-Reference and Hockey-Reference data

  • Model building: pull every season of a player career to train valuation or projection models.
  • Fantasy and betting research: compare per-game production, PER, win shares and BPM across seasons.
  • Payroll and cap analysis: combine a team roster with salary history for value-per-dollar studies.
  • Media and content: get verified season-by-season numbers for articles and graphics.

๐Ÿ”Œ Run Sports Reference Stats Scraper via API, schedule and integrations

Call it from your own code with the Apify API client:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('RecordsData/sports-reference-stats-scraper').call({
startUrls: [{ url: 'https://www.hockey-reference.com/players/o/ovechal01.html' }],
maxItems: 25,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length);

Use the Apify Scheduler to refresh data after each season, send results to Google Sheets, Zapier, Make or n8n, or connect the Actor to AI agents through the Apify MCP server.

The Actor reads publicly available statistics pages and does not log in or bypass any protection. It requests pages one at a time with a delay between them. You are responsible for using the data in line with the target site's terms of use and the laws that apply to you. Sports statistics are not personal data in the sense of GDPR for most use cases, but check your own situation. This is an independent tool and is not affiliated with Sports Reference LLC.

โ“ Frequently asked questions about the Sports Reference Stats Scraper

Which Sports Reference sites does it support?

Basketball-Reference.com (NBA) and Hockey-Reference.com (NHL). Baseball-Reference, Pro-Football-Reference and FBref sit behind a Cloudflare challenge when we checked on 2026-09-15, so they are not supported.

Which URLs can I use?

Player pages such as /players/j/jamesle01.html and team-season pages such as /teams/BOS/2024.html. Other URL types return an error row that is not billed.

Why did I get 0 results?

Either the page does not exist (404) or it has no stats table, for example a player who has not played yet. In that case the run ends as succeeded with a "No rows found" message and you are not charged. If the run fails instead, check the error rows in the dataset: the URL may be unsupported or the site may have changed its layout.

Why do basketball and hockey rows have different fields?

Each sport tracks different stats, and the Actor keeps each site's own column names instead of forcing a shared schema.

Why are some salary or advanced rows missing for older seasons?

The source site does not publish every number for every season, especially salaries before the 1990s. The Actor returns what is on the page.

Can I scrape a whole team roster?

Yes. Use a team-season URL, switch on Team roster, and optionally Advanced stats.

Do I need a proxy?

No. Both sites responded normally in our tests without a proxy, so there is no proxy setting to configure.

How do I limit my spend?

Set Max Items and the maximum cost per run. The Actor stops cleanly when either limit is reached.

๐Ÿ”— Want more sports data? Other PunkRecordsData scrapers

๐Ÿ†˜ Support

Found a bug or need another field? Open an issue in the Issues tab of this Actor or write to contact.punkrecordsdata@gmail.com.

Last updated: 2026-10-03