Historical Source Coverage
Current Strategy
Historical seasons are discovered from the NBA Stats schedule endpoint and archived directly from NBA Stats V3:
scheduleleaguev2defines the final game catalog.playbyplayv3andboxscoretraditionalv3are retained byte-for-byte underdata/raw/stats/.- Processing prefers a validated Stats V3 endpoint artifact.
- A previously retained liveData CDN artifact is used only when its matching V3 endpoint is absent.
The old liveData historical pull is not the active acquisition strategy. Its
coverage was incomplete and unreliable across older seasons. The surviving
liveData files are a legacy cache layer, not a source to probe when extending
history. New historical acquisition goes through nba-fetch-stats-history.
V3 Play-By-Play Boundary
Direct NBA Stats probes found populated playbyplayv3 event streams from
1996-97 onward. Equivalent 1995-96 and earlier requests returned structurally
valid responses with an empty game.actions array. Since possession and lineup
reconstruction require events, 1996-97 is the practical lower boundary for the
V3 archive. Earlier boxscoretraditionalv3 availability does not change that
boundary.
Older schedule responses can include unplayed exhibition and if-necessary placeholders with non-final status and zero-valued team identities. Discovery excludes precisely those rows; final games and identified games still undergo strict schema validation. See ADR-0007.
Regular-Season Source Coverage
This table is generated from the final-game catalog, byte-preserved raw cache,
and player-season bio partitions. Every listed season uses Stats V3 for its
play-by-play and box-score archive except 2019-20*. Source completeness is
not processing eligibility: only games passing the downstream quality contract
are model-ready.
| Season | Catalog regular games | PBP source | Box source | Player-season bios |
|---|---|---|---|---|
| 1996-97 | 1,189 | 1,189 V3 | 1,189 V3 | 441 |
| 1997-98 | 1,189 | 1,189 V3 | 1,189 V3 | 439 |
| 1998-99 | 725 | 725 V3 | 725 V3 | 440 |
| 1999-00 | 1,189 | 1,189 V3 | 1,189 V3 | 439 |
| 2000-01 | 1,189 | 1,189 V3 | 1,189 V3 | 441 |
| 2001-02 | 1,189 | 1,189 V3 | 1,189 V3 | 440 |
| 2002-03 | 1,189 | 1,189 V3 | 1,189 V3 | 428 |
| 2003-04 | 1,189 | 1,189 V3 | 1,189 V3 | 442 |
| 2004-05 | 1,230 | 1,230 V3 | 1,230 V3 | 464 |
| 2005-06 | 1,230 | 1,230 V3 | 1,230 V3 | 458 |
| 2006-07 | 1,230 | 1,230 V3 | 1,230 V3 | 458 |
| 2007-08 | 1,230 | 1,230 V3 | 1,230 V3 | 451 |
| 2008-09 | 1,230 | 1,230 V3 | 1,230 V3 | 445 |
| 2009-10 | 1,230 | 1,230 V3 | 1,230 V3 | 442 |
| 2010-11 | 1,230 | 1,230 V3 | 1,230 V3 | 452 |
| 2011-12 | 990 | 990 V3 | 990 V3 | 478 |
| 2012-13 | 1,229 | 1,229 V3 | 1,229 V3 | 469 |
| 2013-14 | 1,230 | 1,230 V3 | 1,230 V3 | 482 |
| 2014-15 | 1,230 | 1,230 V3 | 1,230 V3 | 492 |
| 2015-16 | 1,230 | 1,230 V3 | 1,230 V3 | 476 |
| 2016-17 | 1,230 | 1,230 V3 | 1,230 V3 | 486 |
| 2017-18 | 1,230 | 1,230 V3 | 1,230 V3 | 540 |
| 2018-19 | 1,230 | 1,230 V3 | 1,230 V3 | 530 |
| 2019-20* | 1,059 | 264 V3 + 795 CDN | 266 V3 + 793 CDN | 529 |
| 2020-21 | 1,080 | 1,078 V3 + 2 CDN | 1,078 V3 + 2 CDN | 540 |
| 2021-22 | 1,230 | 1,228 V3 + 2 CDN | 1,228 V3 + 2 CDN | 605 |
| 2022-23 | 1,230 | 1,228 V3 + 2 CDN | 1,228 V3 + 2 CDN | 539 |
| 2023-24 | 1,230 | 1,228 V3 + 2 CDN | 1,228 V3 + 2 CDN | 572 |
| 2024-25 | 1,230 | 1,228 V3 + 2 CDN | 1,228 V3 + 2 CDN | 569 |
| 2025-26 | 1,230 | 1,230 V3 | 1,230 V3 | 582 |
* The earlier 2019-20 V3 cache is partial, so 795 PBP and 793 box-score responses use the retained liveData CDN fallback. The two CDN fallbacks in 2020-21 through 2024-25 are isolated cache gaps, not a second acquisition strategy.
Source Adaptation
Stats V3 is adapted in memory at the processing boundary. The adapter maps the V3 event vocabulary, splits combined substitution records, preserves player identifiers as strings or integers without float coercion, and normalizes scores and period clocks to the common event schema. Original raw JSON is never rewritten.
The 1996-era archive also uses a few deterministic legacy encodings. When a
box score has exactly five non-empty position values, those players are its
starters; when it lists positions for reserves too, the traditional first-five
box-score ordering defines the starters. Blank score fields and a regressive
0-0 score on non-scoring actions carry forward the preceding cumulative
score. If an otherwise valid period has no start action, the adapter inserts a
derived period-start boundary. These are representation reconciliations, not
changes to the preserved source documents.
Older liveData substitutions can encode only the outgoing player. When that legacy fallback is selected, the source adapter expands its paired identifiers before lineup reconstruction. This is a compatibility path, not the preferred historical representation.
Processing And Quality
Source availability is distinct from modeling eligibility. Every selected game still passes lineup, possession, score-conservation, and period-balance checks. Games with contradictory or insufficient evidence remain named failures or warnings in the quality report; they are never silently repaired.
The regular-season historical RAPM panel uses the approved pass/warning subset
recorded in data/audit/historical_regular/games.parquet. This processing
eligibility is intentionally separate from the raw-coverage table above: the
append-only build ledger and latest data/quality/games.parquet rows are the
authority while the 1996-97 through 2018-19 reconstruction run is in progress.
Only the approved subset is a model input; raw completeness is never presented
as a claim that every catalog game is model-ready.
Postseason
Historical postseason raw responses follow the same Stats V3-first policy. The first playoff-prior ablation currently uses the successfully processed subset of those cached games. Its uneven processing coverage is reported with the experiment in Prior-Centered RAPM, rather than being represented as complete historical playoff coverage.
Reproduce
Fetch V3 responses for a season type directly from the historical catalog:
uv run nba-fetch-stats-history \
--season 2024-25 \
--season-type regular \
--endpoint playbyplayv3 \
--endpoint boxscoretraditionalv3 \
--max-workers 2
The raw cache is the resume boundary. Endpoint-level fetch provenance is stored
in data/manifests/stats_fetches.parquet; processing provenance and selected
source hashes are stored with per-game outputs.