# Sports Reference Stats Scraper - NBA & NHL (`punkrecordsdata/sports-reference-stats-scraper`) Actor

Scrape NBA player and team stats from Basketball-Reference and NHL stats from Hockey-Reference: per-season stats, advanced metrics, rosters and salaries. Export to CSV, JSON, Excel, or XML.

- **URL**: https://apify.com/punkrecordsdata/sports-reference-stats-scraper.md
- **Developed by:** [PunkRecordsData](https://apify.com/punkrecordsdata) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $21.00 / 1,000 player season records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

<p align="center">
  <img src="https://api.apify.com/v2/key-value-stores/AAm3a1h3Z9nYfrvh9/records/banner" alt="PunkRecordsData" width="100%" />
</p>

## 🏀 Sports Reference Stats Scraper - NBA & NHL

> 🚀 **Get real NBA and NHL stats in seconds.** Scrape per-season stats, advanced metrics, team rosters and salaries from Basketball-Reference and Hockey-Reference. Export to CSV, JSON, Excel, or XML.

The **Sports Reference Stats Scraper** extracts player and team statistics from [Basketball-Reference.com](https://www.basketball-reference.com) and [Hockey-Reference.com](https://www.hockey-reference.com), the two most cited historical stats sources in sports analytics. Point it at any player or team page and get every season of real stats back, structured and ready to use.

Each player page returns every season of real per-game stats, advanced metrics (PER, win shares, BPM for NBA; possession and rate stats for NHL), and salary history where published. Team pages add the full roster, one row per player.

| 🎯 Target Audience | 💡 Primary Use Cases |
|---|---|
| Sports analysts & bettors | Build models on real historical per-season stats |
| Fantasy sports players | Compare advanced metrics across seasons |
| Sports media & bloggers | Pull verified stats without manual copy-paste |
| Data scientists | Train models on structured NBA/NHL history |

### 📋 What the Sports Reference Stats Scraper does

> 💡 **Why it matters:** most public stats scrapers cover one stat table and call it done. This one reads per-season stats, advanced metrics, roster and salary data from the same page, across two different sports with two different stat schemas, without missing the tables that most scrapers of these sites silently drop.

Point it at a player page (e.g. `/players/j/jamesle01.html`) or a team-season page (e.g. `/teams/BOS/2024.html`), and it extracts every season row from the site's own stats tables, tagged by sport and page type.

### 📊 Output

Each record contains (fields vary by sport and table, since NBA and NHL track different stats):

| Field | Description |
|---|---|
| `sport` | `basketball` or `hockey` |
| `pageType` | `player` or `team` |
| `name` | Player or team name |
| `url` | Source page URL |
| `year_id` | Season, e.g. `2003-04` |
| *(sport-specific stat fields)* | Every column the source table publishes for that season |
| `scrapedAt` | ISO timestamp of scrape |

**Example output** (real player, real stats, 2026-09-19):

```json
{
  "sport": "basketball",
  "pageType": "player",
  "name": "LeBron James",
  "url": "https://www.basketball-reference.com/players/j/jamesle01.html",
  "year_id": "2003-04",
  "age": "19",
  "team_name_abbr": "CLE",
  "pos": "SG",
  "games": "79",
  "pts_per_g": "20.9",
  "ast_per_g": "5.9",
  "trb_per_g": "5.5",
  "awards": "MVP-9,ROY-1",
  "scrapedAt": "2026-09-19T19:54:28.377Z"
}
```

### ✨ Why choose this Actor

- **Two sports, one actor**, NBA (Basketball-Reference) and NHL (Hockey-Reference), each with its own real stat schema, not a generic template forced onto both.
- **4 real billable events**, per-season stats, advanced metrics, roster and salaries, each optional. Most competitors on this niche charge one flat price for one generic row.
- **Reads the tables competitors miss**, several stat tables on these sites (salaries included) are wrapped in HTML comments and only render after JavaScript runs; a scraper that doesn't account for this silently returns nothing for them.
- **Works on both player and team pages** from a single input list.

### 🚀 How to use

1. **Create a free Apify account** at [console.apify.com/sign-up](https://console.apify.com/sign-up), you get $5 credit monthly.
2. **Open the Sports Reference Stats Scraper** in Apify Console.
3. **Add start URLs**, paste player or team pages:
   - `https://www.basketball-reference.com/players/j/jamesle01.html`
   - `https://www.hockey-reference.com/players/o/ovechal01.html`
   - `https://www.basketball-reference.com/teams/BOS/2024.html`
4. **Set max items**, how many season rows to scrape (free tier caps at 10).
5. **Toggle the extras you need**, Advanced stats, Team roster, Salaries, each is billed only when enabled.
6. **Click Start**, results appear in the Output tab within minutes.
7. **Download** as JSON, CSV, Excel, or XML.

### 💼 Business use cases

#### Historical model building

Pull every season of a player's career in one call to train prediction or valuation models.

#### Advanced metrics comparison

Compare PER, win shares and BPM (NBA) or possession/rate stats (NHL) across players and seasons.

#### Roster and payroll research

Scrape a team-season page for the full roster, then layer salary history on top for cap and value analysis.

#### Content and media

Pull verified season-by-season stats for articles, graphics or fantasy sports write-ups without manual lookup.

### 🔌 Automating Sports Reference Stats Scraper

- **Make.com / Zapier**, Schedule scrapes after each season and push results to Google Sheets
- **Slack**, Get notified when new season data is available
- **Airbyte**, Feed stats into your data warehouse
- **GitHub Actions**, Automate scrapes on a cron schedule

### 🌟 Beyond business use cases

- **Personal fantasy research**, build your own player comparison sheets
- **Academic research**, study performance trends across NBA/NHL history
- **Non-profit / educational**, build teaching datasets from verified public stats

### 🤖 Ask an AI assistant about this scraper

Ask questions about how to use this scraper, what data it extracts, or how to integrate it with your workflow. The AI assistant has full context about the actor's capabilities.

### ❓ Frequently Asked Questions

#### Which sites does this cover?

Basketball-Reference.com (NBA) and Hockey-Reference.com (NHL) only. The other three Sports Reference family sites (Baseball-Reference, Pro-Football-Reference, FBref) are behind a Cloudflare challenge and not covered by this actor.

#### Why do basketball and hockey records have different fields?

Each sport tracks different stats natively (field goal percentage vs. faceoff percentage, for example). This actor reads each site's own real columns rather than forcing both sports into one artificial schema.

#### How does pricing work?

The base `player-season-record` event bills per season row scraped. Enable Advanced stats, Team roster, and/or Salaries for additional data at extra per-event cost. Free users get 10 rows per run.

#### Is scraping these sites legal?

This actor scrapes publicly available statistics. Respect the target site's terms of service and use the data responsibly.

#### Can I scrape a whole team's season in one call?

Yes, paste the team-season URL (e.g. `/teams/BOS/2024.html`) and enable Team roster for the full player list.

#### What happens if I hit the free tier limit?

The actor caps output at 10 items for free users. Upgrade to a paid plan for up to 1,000,000 items.

#### Why are some salary/advanced rows missing on older seasons?

The source site itself doesn't publish every data point for every historical season (salaries in particular are often missing pre-1990s). The actor returns exactly what the source publishes.

#### Can I schedule recurring scrapes?

Yes, use Apify's Scheduler to run the actor daily, weekly, or on any cron schedule.

### 🔌 Integrate with any app

The actor outputs JSON datasets that work with virtually any tool: Google Sheets, Airtable, Notion, databases (PostgreSQL, MongoDB), data pipelines (Airbyte, Fivetran), and more. Use the Apify API to pull data programmatically.

### 🔗 Recommended Actors

- [SofaScore Scraper](https://apify.com/punkrecordsdata/sofascore-match-stats-scraper), Live sports match statistics
- [Basketball Match Stats Scraper](https://apify.com/punkrecordsdata/basketball-match-stats-scraper), Live NBA/EuroLeague box scores and play-by-play
- [Prediction Markets Scraper](https://apify.com/punkrecordsdata/prediction-markets-scraper), Kalshi & Polymarket odds

> 💡 **Pro Tip:** browse the complete [PunkRecordsData collection](https://apify.com/punkrecordsdata).

**🆘 Need Help?** contact.punkrecordsdata@gmail.com

> **⚠️ Disclaimer:** independent tool, not affiliated with Sports Reference LLC; only publicly available data.

# Actor input Schema

## `startUrls` (type: `array`):

Player or team pages from basketball-reference.com or hockey-reference.com, e.g. basketball-reference.com/players/j/jamesle01.html or hockey-reference.com/teams/WSH/2024.html

## `maxItems` (type: `integer`):

Free users: limited to 10 items (preview). Paid users: optional, max 1,000,000. Bounds the primary player-season-record event only.

## `includeAdvanced` (type: `boolean`):

Advanced metrics per season (PER/WS/BPM for basketball, possession/rate stats for hockey).

## `includeRoster` (type: `boolean`):

Full roster for a team page, one row per player (only applies to team URLs).

## `includeSalaries` (type: `boolean`):

Salary history per season, where published (basketball-reference only).

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.basketball-reference.com/players/j/jamesle01.html"
    }
  ],
  "maxItems": 10,
  "includeAdvanced": false,
  "includeRoster": false,
  "includeSalaries": false
}
```

# Actor output Schema

## `overview` (type: `string`):

Key fields: player/team, season, sport

## `fullData` (type: `string`):

Complete dataset including advanced stats, roster and salary rows

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://www.basketball-reference.com/players/j/jamesle01.html"
        }
    ],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("punkrecordsdata/sports-reference-stats-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": [{ "url": "https://www.basketball-reference.com/players/j/jamesle01.html" }],
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("punkrecordsdata/sports-reference-stats-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://www.basketball-reference.com/players/j/jamesle01.html"
    }
  ],
  "maxItems": 10
}' |
apify call punkrecordsdata/sports-reference-stats-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,punkrecordsdata/sports-reference-stats-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/yPECcipvVpjgRXgk3/builds/qaURTY63ldsSugo3b/openapi.json
