# Basketball Reference Scraper (`normdata/basketball-reference-scraper`) Actor

Get NBA/ABA data from Basketball-Reference: full career stats by player name, a season's full leaderboard by stat type, team rosters, draft classes with career totals, and MVP/ROY/DPOY/All-NBA voting for any season back to 1950. No login, no API key needed.

- **URL**: https://apify.com/normdata/basketball-reference-scraper.md
- **Developed by:** [Norm Data](https://apify.com/normdata) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.00 / 1,000 results

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

![Norm Data](https://i.ibb.co/rGbhM5Y8/Chat-GPT-Image-Sep-8-2026-02-20-50-PM.png)

## Basketball Reference Scraper

Get NBA/ABA data from **Basketball-Reference.com** with no login: full career stats by player
name, a whole season's leaderboard by stat type, a team's roster and stats, a draft class with
career totals, or MVP/ROY/DPOY/All-NBA voting for any season back to 1950.

Here is one real row (player mode, one season of a career):

```json
{
  "player_id": "jamesle01",
  "name": "LeBron James",
  "player_url": "https://www.basketball-reference.com/players/j/jamesle01.html",
  "position": "Small Forward, Power Forward, Point Guard, Center, and Shooting Guard",
  "shoots": "Right",
  "height": "6-9",
  "weight_lb": 250,
  "birth_date": "December 30, 1984",
  "birth_place": "Akron, Ohio",
  "high_school": "St. Vincent-St. Mary in Akron, Ohio",
  "draft": "Cleveland Cavaliers, 1st round (1st pick, 1st overall), 2003 NBA Draft",
  "career_per_game": [
    { "season": "2003-04", "team": "CLE", "games": 79, "points_per_game": 20.9, "assists_per_game": 5.9 }
  ],
  "career_advanced": [{ "season": "2003-04", "per": 18.3, "win_shares": 5.1, "value_over_replacement": 2.9 }],
  "scraped_at": "2026-09-12T00:00:00.000Z"
}
```

### What it does

Reads Basketball-Reference.com's own public pages directly and normalizes every table into clean,
flat rows.

- **Player** resolves a name (via Basketball-Reference's own search) or a direct ID to full career
  stats: bio, per-game/totals/advanced by season, and playoffs.
- **Season** returns a whole year's leaderboard for one stat type (per game, totals, advanced, per
  36 minutes, or per 100 possessions), with server-side-equivalent filters for team, minimum games,
  and minimum minutes per game.
- **Team** returns one team-season's roster joined with that season's per-game stats.
- **Draft** returns a full draft class with career totals and advanced metrics through the present.
- **Awards** returns MVP/ROY/DPOY/SMOY/MIP/Clutch POY/All-NBA/All-Defense/All-Rookie/Coach of the
  Year voting for one season, including real vote share.

Missing source values are returned as `null`, never invented.

### Why this scraper

- **Every mode both real competitors split across two separate Actors, plus three neither has.**
  One competitor does player career stats; the other does season leaderboards. Neither has team
  rosters, draft classes, or awards voting - this Actor covers all five in one place.
- **Real bugs caught before shipping, not left in.** Basketball-Reference appends a non-player
  "League Average" summary row to every season leaderboard - excluded here, confirmed live rather
  than assumed. A raw field code (`g`) genuinely means different things on different tables
  (career games on a draft page, that season's team games on a Coach of the Year row) - handled
  correctly on each, not treated as one universal rename.
- **Handles Basketball-Reference's own anti-scrape layout.** A real, confirmed subset of its stat
  tables are wrapped in HTML comments (which tables varies by page) - unwrapped generically here,
  not hardcoded to one page's specific table list.
- **No API key, no login, no browser.** Reads only what a normal visitor's page load reads.

### How it compares

Checked directly against both real Basketball-Reference scrapers on Apify (their own declared
input/output, not a guess):

| Capability | This actor | lulzasaur/basketball-reference-scraper | parseforge/basketball-reference-scraper |
|---|:--:|:--:|:--:|
| Player career stats (per-game, totals, advanced, playoffs) | yes | yes | **no** |
| Season leaderboard (all players, one stat type) | yes | **no** | yes |
| **Team rosters** | **yes** | no | no |
| **Draft classes** | **yes** | no | no |
| **Awards voting (MVP/ROY/DPOY/All-NBA/etc.)** | **yes** | no | no |
| Season leaderboard filters (team, min games, min minutes) | yes | n/a | **no** |
| `maxItems` cap | yes | not documented | yes |
| Declared Apify dataset schema | yes | not documented | not documented |

### Use cases

- **Fantasy basketball.** Per-game averages and season leaderboards to build rankings.
- **Historical analysis.** Compare eras with career per-game, totals, and advanced stats.
- **Draft research.** A draft class's real career output, ranked by pick.
- **Awards/HOF projection.** Real MVP/DPOY/All-NBA voting share, not just who won.
- **Team-building and roster analysis.** A team-season's full roster with real production per player.
- **Sports journalism and content.** Structured stats ready for a chart, table, or story.

### Quickstart

Player career stats by name:

```json
{ "mode": "player", "playerNames": ["LeBron James", "Nikola Jokić"] }
```

Season leaderboard: Boston players in 2023-24, 20+ games and 15+ minutes per game.

```json
{ "mode": "season", "year": 2024, "statType": "per_game", "team": "BOS", "minGames": 20, "minMinutesPerGame": 15 }
```

A team's full roster and stats:

```json
{ "mode": "team", "teamCode": "BOS", "teamYear": 2024 }
```

A draft class:

```json
{ "mode": "draft", "draftYear": 2020 }
```

A season's awards voting:

```json
{ "mode": "awards", "awardsYear": 2024 }
```

### Input reference

| Field | Applies to | Description |
|---|---|---|
| `maxItems` | all | Ceiling on rows written. |
| `mode` | all | `player`, `season`, `team`, `draft`, or `awards`. |
| `playerNames` / `playerIds` | player | Names (resolved via Basketball-Reference's own search) or direct player codes. |
| `year` / `statType` | season | Season end year (e.g. 2024 for 2023-24) and `per_game`/`totals`/`advanced`/`per_minute`/`per_poss`. |
| `team` / `minGames` / `minMinutesPerGame` | season | Optional filters on the leaderboard. |
| `teamCode` / `teamYear` | team | 3-letter team code and season end year. |
| `draftYear` | draft | The draft class's year. |
| `awardsYear` | awards | The season's end year. |

### Output reference (selected)

| Field | Description |
|---|---|
| `player_id` / `name` / `player_url` | Identity (player mode). |
| `position` / `shoots` / `height` / `weight_lb` / `birth_date` / `birth_place` / `college` / `high_school` / `draft` | Bio (player mode). |
| `career_per_game` / `career_totals` / `career_advanced` / `playoffs_per_game` | One entry per season (player mode). |
| `player_id` / `player_name` / `team` / stat fields | Season, team, and draft mode rows. |
| `category` / `rank` / `coach_name` / `votes_first` / `points_won` / `points_max` / `award_share` | Awards mode. |
| `all_nba_team` / `all_defense_team` / `all_rookie_team` / `first_team_votes` / `second_team_votes` / `third_team_votes` | Awards mode: All-NBA/All-Defense/All-Rookie team voting only. |
| `per` / `win_shares` / `box_plus_minus` / `value_over_replacement` | Advanced metrics (PER, WS, BPM, VORP - Basketball-Reference's own terms). |
| `query` / `error` | Set on an unresolved player lookup. |
| `scraped_at` | ISO 8601 collection timestamp. |

### Run via API and CLI

```bash
curl -X POST "https://api.apify.com/v2/acts/USERNAME~basketball-reference-scraper/run-sync-get-dataset-items?token=<TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"mode":"player","playerNames":["LeBron James"]}'
```

```bash
apify call USERNAME/basketball-reference-scraper --input '{"mode":"season","year":2024,"statType":"per_game"}'
```

### Fetch results

```bash
curl "https://api.apify.com/v2/datasets/<DATASET_ID>/items?token=<TOKEN>&clean=true&format=json"
curl "https://api.apify.com/v2/datasets/<DATASET_ID>/items?token=<TOKEN>&clean=true&format=csv"
```

### Billing and limits

- **Pay per result.** Billed per row written (one row per player in player mode; one row per
  player/pick/award-line in season/team/draft/awards mode).
- **No charge on failure.** A run that produces zero rows fails with a message and is not billed.
- An unresolved player name/ID still writes one `error: "not found"` row and is billed as a base row.
- **Free Apify plans** run the built-in 10-row sample only.

### FAQ and troubleshooting

**Do I need an API key?** No. Basketball-Reference's own public pages are read directly, with no
key and no login.

**Why did my player search return "not found"?** The name didn't match anything in
Basketball-Reference's own search. Try a more complete name, or use `playerIds` with the exact
code from the player's URL (e.g. `jamesle01`).

**Why does a season leaderboard have more rows than real players?** A player traded mid-season
gets one row per team plus a "TOT" (total) row - this is Basketball-Reference's own real
convention, not a duplicate. Basketball-Reference's own trailing "League Average" summary row is
excluded (it isn't a player).

**Why is `career_games` sometimes labeled `games` instead?** In draft mode, that field really is a
career total. In awards mode's Coach of the Year rows, the identical raw source field means that
season's team games - kept as `games` there rather than mislabeled as a career total.

**What are PER, WS, BPM, and VORP?** Basketball-Reference's own advanced metrics: Player
Efficiency Rating, Win Shares, Box Plus/Minus, and Value Over Replacement Player. Passed through
under their real, industry-standard names, not invented abbreviations.

**How current is the data?** Read live from Basketball-Reference.com on every run. It is not cached.

**Is this an official NBA/Basketball-Reference tool?** No. Independent, not affiliated with either.
It reads only publicly available pages.

### Local development

```bash
bun install
bun test                       # offline: table parsing, field mapping, bio parsing (real fixtures)
bun run src/main.ts             # reads storage/key_value_stores/default/INPUT.json
```

# Actor input Schema

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

Caps how many rows this run writes. Starts at 10 for a quick sample; raise it for a full run.

## `mode` (type: `string`):

Player returns full career stats for specific players. Season returns every player's stats for one year. Team returns one team's roster and stats. Draft returns one draft class with career totals. Awards returns MVP/ROY/DPOY/All-NBA/etc. voting for one season.

## `playerNames` (type: `array`):

Resolved via Basketball-Reference's own player search, e.g. "LeBron James", "Michael Jordan".

## `playerIds` (type: `array`):

Basketball-Reference's own player code (the segment before .html in a player's URL, e.g. "jamesle01" for LeBron James). Skips the name search.

## `year` (type: `integer`):

The season's end year, e.g. 2024 for the 2023-24 season (Basketball-Reference's own convention). Any season from 1950 onward.

## `statType` (type: `string`):

Which stat table to pull for the whole season's leaderboard.

## `team` (type: `string`):

3-letter team code (e.g. BOS, LAL) to only keep that team's players from the season leaderboard. Leave empty for every team.

## `minGames` (type: `integer`):

Only keep players who played at least this many games that season.

## `minMinutesPerGame` (type: `number`):

Only keep players averaging at least this many minutes per game.

## `teamCode` (type: `string`):

3-letter Basketball-Reference team code, e.g. BOS, LAL, GSW.

## `teamYear` (type: `integer`):

The season's end year for this team's roster, e.g. 2024 for 2023-24.

## `draftYear` (type: `integer`):

The draft class's year. Career stats shown are through the most recently completed season.

## `awardsYear` (type: `integer`):

The season's end year for MVP/ROY/DPOY/All-NBA/etc. voting, e.g. 2024 for 2023-24.

## Actor input object example

```json
{
  "maxItems": 10,
  "mode": "player",
  "playerNames": [
    "LeBron James"
  ],
  "year": 2024,
  "statType": "per_game",
  "teamCode": "BOS",
  "teamYear": 2024,
  "draftYear": 2020,
  "awardsYear": 2024
}
```

# Actor output Schema

## `listings` (type: `string`):

Complete dataset with all scraped rows.

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

Overview view with key fields displayed in a table.

# 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 = {
    "maxItems": 10,
    "mode": "player",
    "playerNames": [
        "LeBron James"
    ],
    "year": 2024,
    "statType": "per_game",
    "teamCode": "BOS",
    "teamYear": 2024,
    "draftYear": 2020,
    "awardsYear": 2024
};

// Run the Actor and wait for it to finish
const run = await client.actor("normdata/basketball-reference-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 = {
    "maxItems": 10,
    "mode": "player",
    "playerNames": ["LeBron James"],
    "year": 2024,
    "statType": "per_game",
    "teamCode": "BOS",
    "teamYear": 2024,
    "draftYear": 2020,
    "awardsYear": 2024,
}

# Run the Actor and wait for it to finish
run = client.actor("normdata/basketball-reference-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 '{
  "maxItems": 10,
  "mode": "player",
  "playerNames": [
    "LeBron James"
  ],
  "year": 2024,
  "statType": "per_game",
  "teamCode": "BOS",
  "teamYear": 2024,
  "draftYear": 2020,
  "awardsYear": 2024
}' |
apify call normdata/basketball-reference-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,normdata/basketball-reference-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/pzXPEhgnOVZ4PbRkB/builds/l1byfJvcPuyKl2ZUW/openapi.json
