Skip to main content
Version: 0.0.54

The parser layer

Parser-backed wrappers return a tidy polars DataFrame by default (0.0.54+); pass return_parsed=False to recover the raw Dict. The parser layer in sportsdataverse._common_espn_parsers turns those payloads into tidy polars (or pandas) DataFrames. Parsers are league-agnostic: the same parser handles MLB, NFL, NBA, WBB, etc. because ESPN's payload shapes are identical across leagues.

Two ways to invoke a parser

Wrappers whose short name is in ENDPOINT_PARSERS return a polars DataFrame by default (0.0.54+). Pass return_parsed=False for the raw Dict, or return_as_pandas=True for pandas:

from sportsdataverse.nba import espn_nba_team_roster

df = espn_nba_team_roster(team_id=13) # → polars (default)
pdf = espn_nba_team_roster(team_id=13, return_as_pandas=True) # → pandas
raw = espn_nba_team_roster(team_id=13, return_parsed=False) # → raw Dict

This is the shortest path and the right default for most callers.

2. Direct parser call (works for any payload)

You can always parse a previously-fetched payload — useful when chaining calls or operating on cached responses:

from sportsdataverse._common_espn_parsers import parse_team_roster
from sportsdataverse.nba import espn_nba_team_roster

raw = espn_nba_team_roster(team_id=13)
df = parse_team_roster(raw)
df = parse_team_roster(raw, return_as_pandas=True)

The 18 parsers

ParserEndpoint familyOutput shape
parse_scoreboard*_scoreboardOne row per event
parse_teams*_teams_site, *_teams_coreOne row per team
parse_standings*_standings, *_standings_coreOne row per team standing
parse_groups*_conferencesOne row per group, flattened depth
parse_athlete_overview*_athlete_overview (Web v3)Single-entity summary
parse_athlete_stats*_athlete_statsOne row per stat category
parse_athlete_gamelog*_athlete_gamelogOne row per game
parse_athlete_splits*_athlete_splitsOne row per split
parse_leaders*_leadersOne row per (category × leader)
parse_coaches*_coaches, *_season_coachesOne row per coach
parse_draft*_season_draftOne row per pick
parse_event_competitor_roster*_event_competitor_rosterOne row per athlete
parse_event_competitor_statistics*_event_competitor_statisticsOne row per stat
parse_event_competitor_linescores*_event_competitor_linescoresOne row per period
parse_event_plays*_event_playsOne row per play
parse_team_schedule*_team_scheduleOne row per event
parse_team_roster*_team_rosterOne row per athlete
parse_news*_news, *_team_news, *_athlete_newsOne row per article
parse_injuries*_injuries, *_team_injuries, *_athlete_injuriesOne row per team with injuries
parse_itemsGeneric — Core v2 paginated {items: [...]} + Core v2 {entries: [...]} (athlete_statisticslog, etc.)One row per item
parse_summarySite v2 summary (dispatcher)Dict of 18 sub-frames
parse_summary_boxscore_playersummary section: per-athlete boxscoreOne row per (team × athlete)
parse_summary_boxscore_teamsummary section: per-team boxscoreOne row per (team × stat)
parse_summary_playssummary section: play-by-playOne row per play
parse_summary_winprobabilitysummary section: win-prob over timeOne row per probability tick
parse_summary_leaderssummary section: per-game stat leadersOne row per (team × category × leader)
parse_summary_game_infosummary section: venue + attendanceSingle row
parse_summary_officialssummary section: refs / umpiresOne row per official
parse_summary_headersummary section: event header + competition shellSingle row
parse_summary_season_seriessummary section: head-to-head seriesOne row per series entry
parse_summary_against_the_spreadsummary section: per-team ATS recordsOne row per (team × record)
parse_summary_standingssummary section: league standings snapshotOne row per team standing
parse_summary_broadcastssummary section: TV broadcastsOne row per broadcast (sparse on past games)
parse_summary_formatsummary section: game format (regulation + OT)Single row
parse_summary_pickcentersummary section: pre-game odds / picksOne row per provider (sparse)
parse_summary_oddssummary section: odds providers / marketsOne row per entry (sparse)
parse_summary_articlesummary section: recap article metadataSingle row
parse_summary_injuriessummary section: per-team injury listsOne row per team
parse_summary_newssummary section: embedded news feedOne row per article
parse_summary_drivessummary section: NFL/CFB drives (drives.previous[])One row per drive
parse_summary_drive_playssummary section: NFL/CFB plays nested under drivesOne row per play across every drive, with drive_id + drive_sequence for joinability
parse_summary_scoring_playssummary section: NFL/CFB scoring summaryOne row per scoring play

Contract guarantees

Every parser obeys these rules:

  1. Returns polars by default; pandas.DataFrame via return_as_pandas=True.
  2. Empty / malformed payloads return a zero-row frame rather than raising — callers guard the height == 0 case.
  3. Output columns are snake-cased via sportsdataverse.dl_utils.underscore (e.g. displayNamedisplay_name, shotsOnGoalshots_on_goal).
  4. Uses pandas.json_normalize for flattening then converts to polars at the end. Mixed-type object columns are stringified to keep polars ingestion clean.

ENDPOINT_PARSERS registry

The registry maps the short name in _common_espn's wrapper tables to its parser. All 121 wrapper short names are registered — every factory-bound wrapper across all 8 leagues gains the return_parsed=True shim automatically.

>>> from sportsdataverse._common_espn_parsers import ENDPOINT_PARSERS, parser_for
>>> parser_for("scoreboard").__name__
'parse_scoreboard'
>>> parser_for("venue").__name__
'parse_single_entity'
>>> len(ENDPOINT_PARSERS)
121

The registry uses three generic parsers as fall-throughs for endpoint families that share a shape:

  • parse_single_entity — for any Core v2 single-resource payload (team, venue, franchise, coach, award, position, season_info, athlete_core, event_competitor, etc.). Returns a one-row frame flattened via pandas.json_normalize.
  • parse_items — for any Core v2 paginated {items: [...]} or Core v2 {entries: [...]} payload (athlete_statisticslog, calendar variants, draft, event lists, season_powerindex, talentpicks, etc.).
  • parse_summary — for the rich Site v2 summary dispatcher (returns a dict of 20 sub-frames).

Adding a new parser

  1. Write the parser in sportsdataverse/_common_espn_parsers.py following the existing pattern (empty-payload guard, pd.json_normalize, _snake_columns, _to_output).
  2. Add an entry to ENDPOINT_PARSERS mapping the short name to the parser.
  3. The return_parsed=True shim picks it up automatically on next import — no extension-module changes needed.
  4. Drop a captured fixture in tests/fixtures/espn/ and a test in tests/test_espn_universal_parsers.py.

See also