The core-v2-sourced successor to espn_cfb_pbp(). Returns
one tidy row per play for a single college football game, optionally with
the full Expected Points Added (EPA) / Win Probability Added (WPA)
modeling attached. It supersedes the legacy site-v2 espn_cfb_pbp():
instead of parsing the deeply-nested site-v2 summary feed, it sources
the play-by-play from the structured ESPN core-v2 drives endpoint in a
single request via espn_cfb_game_drives() – one structured call rather
than a sprawling summary parse, so it is faster and more robust to the
site-v2 feed's column drift.
Arguments
- game_id
(Integer required): ESPN game identifier.
- epa_wpa
(Logical): when
TRUE, run the full EPA/WPA modeling pipeline and return the modeled frame; whenFALSE(default) return the assembled core-v2 play-by-play frame.- output
(Character): controls the modeled-output column set when
epa_wpa = TRUE. Ignored whenepa_wpa = FALSE. Defaults to"default". Must be one of:"default"(recommended) – drops pipeline lag/lead intermediates, redundant alternates (sack_vec,turnover_indicator,kick_play,missing_yard_flag), and drive-result aliases (drive_result2,drive_result_detailed_flag,lag_drive_result_detailed,lead_drive_result_detailed,lag_new_drive_pts). Keepsorig_play_typeandpts_scored(they carry useful per-play information distinct from the canonical columns) and the per-branch WPA scratchpad. ~75 columns lighter than"full"."lean"– everything"default"drops, plus the WPA computation scratchpad. For dashboards / game logs."full"– legacy behavior, drops only the player-name aliases.
Value
A data frame with one row per play. When epa_wpa = FALSE, the
assembled core-v2 play-by-play frame:
| col_name | types | description |
| game_id | character | ESPN game identifier. |
| play_id | character | ESPN play id. |
| type_text | character | Play type label (e.g. Rush, Pass Reception). |
(plus the full espn_cfb_game_drives() plays = "expand" column set –
play start/end down, distance, yard line, clock, scores, the drive_*
drive context, team detail – and the attached game-metadata columns
season, season_type, week, neutral_site,
conference_competition, game_date, home_team_id, home_team,
away_team_id, away_team).
When epa_wpa = TRUE, every column from the epa_wpa = FALSE frame
above (game metadata, drive_* context, team detail and the full
espn_cfb_game_drives() play schema) plus the modeled EPA/WPA
columns produced by the existing pipeline – ep_before, ep_after,
EPA, wp_before, wp_after, wpa and the supporting probability and
cumulative columns. The epa_wpa = TRUE columns are a strict superset
of the epa_wpa = FALSE columns, and the row count is unchanged.
Details
Play-by-play assembly (always). The play rows come from
espn_cfb_game_drives(game_id, plays = "expand", team_detail = TRUE) –
one row per play, in the full espn_cfb_game_pbp() schema, with each
play's drive-level context carried alongside (drive_* columns) and the
ESPN team catalog joined onto every team-id column. Game-level metadata
matching the legacy espn_cfb_pbp() output – season, season_type,
week, neutral_site, conference_competition, game_date, and the
home_team_id / home_team / away_team_id / away_team (plus
abbreviations) – is attached from the core-v2 event resource and
espn_cfb_game_teams().
Game-meta bridge. Season / season_type / week / neutral_site /
conference_competition / game_date and the full home/away block –
including *_team_name, *_team_color, *_team_alternate_color, and
the week-specific *_team_rank – are sourced via
.espn_pbp_game_meta(), which queries the core-v2 event resource. This
makes espn_cfb_pbp_v2() a strict meta-column superset of legacy
espn_cfb_pbp().
Native participant columns. This wrapper requests
participants = "wide" and team_participants = "wide" from
espn_cfb_game_drives(), so the play frame carries type-keyed
{type}_player_id / {type}_player_name / {type}_player_position
columns (e.g. passer_player_id, rusher_player_id, tackler_player_id)
and {type}_team_* columns. Wide mode also emits the
{type}_player_ids / {type}_player_names list-columns; downstream
parquet/CSV writers must drop or stringify these before serialization.
One additional roster fetch + position-catalog fetch per game.
epa_wpa behaviour. When epa_wpa = FALSE (default) the assembled
core-v2 frame is returned as-is. When epa_wpa = TRUE the core-v2
columns are mapped into the exact column schema the legacy
espn_cfb_pbp() hands to its EPA/WPA pipeline, and the same modeling
stack is run verbatim – penalty_detection(), add_play_counts(),
clean_pbp_dat(), clean_drive_dat(), add_yardage(),
add_player_cols(), prep_epa_df_after(), create_epa() and
create_wpa_naive(). The statistical models (the mgcv GAMs, the
multinomial EP model, the FG model and the WP model shipped with the
package) are reused unchanged; this wrapper only supplies the column
adapter between the core-v2 play schema and the modeling input contract.
The modeled EPA/WPA columns the pipeline emits – ep_before,
ep_after, EPA, wp_before, wp_after, wpa, and the supporting
probability and cumulative columns – are then joined back onto the
full epa_wpa = FALSE context frame by play_id. The
epa_wpa = TRUE output is therefore a strict superset of the
epa_wpa = FALSE output: every game-metadata and drive-context column
the default path produces, plus the EPA/WPA model columns. The join is a
left join from the context frame, so no play row is dropped and the row
count is identical to the epa_wpa = FALSE result.
See also
Other ESPN CFB Functions:
espn_cfb_award(),
espn_cfb_awards(),
espn_cfb_clear_cache(),
espn_cfb_coach(),
espn_cfb_coach_record(),
espn_cfb_coaches(),
espn_cfb_franchise(),
espn_cfb_franchises(),
espn_cfb_futures(),
espn_cfb_game_broadcasts(),
espn_cfb_game_drive_plays(),
espn_cfb_game_drives(),
espn_cfb_game_leaders(),
espn_cfb_game_odds(),
espn_cfb_game_pbp(),
espn_cfb_game_play(),
espn_cfb_game_player_box(),
espn_cfb_game_player_statistics(),
espn_cfb_game_powerindex(),
espn_cfb_game_predictor(),
espn_cfb_game_probabilities(),
espn_cfb_game_situation(),
espn_cfb_game_status(),
espn_cfb_game_team_leaders(),
espn_cfb_game_team_linescores(),
espn_cfb_game_team_records(),
espn_cfb_game_team_roster(),
espn_cfb_game_team_statistics(),
espn_cfb_game_teams(),
espn_cfb_groups(),
espn_cfb_player(),
espn_cfb_player_career_stats(),
espn_cfb_player_eventlog(),
espn_cfb_player_gamelog(),
espn_cfb_player_overview(),
espn_cfb_player_seasons(),
espn_cfb_player_splits(),
espn_cfb_player_stats_v3(),
espn_cfb_players(),
espn_cfb_position(),
espn_cfb_positions(),
espn_cfb_powerindex(),
espn_cfb_qbr(),
espn_cfb_rankings(),
espn_cfb_recruits(),
espn_cfb_season_info(),
espn_cfb_season_types(),
espn_cfb_season_weeks(),
espn_cfb_seasons(),
espn_cfb_standings(),
espn_cfb_team(),
espn_cfb_team_ats(),
espn_cfb_team_awards(),
espn_cfb_team_coaches(),
espn_cfb_team_events(),
espn_cfb_team_leaders(),
espn_cfb_team_powerindex(),
espn_cfb_team_ranks(),
espn_cfb_team_record(),
espn_cfb_team_roster(),
espn_cfb_team_schedule(),
espn_cfb_teams(),
espn_cfb_unnest_plays(),
espn_cfb_venue(),
espn_cfb_venues(),
espn_cfb_week_rankings()
Examples
# \donttest{
try(espn_cfb_pbp_v2(game_id = 401628339, epa_wpa = TRUE))
#> ── Play-by-play data from ESPN (core-v2) ───────────────────── cfbfastR 2.3.0 ──
#> ℹ Data updated: 2026-06-09 07:56:45 UTC
#> # A tibble: 156 × 449
#> season id_play game_id game_play_number half_play_number drive_play_number
#> <int> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 2024 401628339… 401628… 1 1 1
#> 2 2024 401628339… 401628… 2 2 2
#> 3 2024 401628339… 401628… 3 3 3
#> 4 2024 401628339… 401628… 3 3 3
#> 5 2024 401628339… 401628… 4 4 4
#> 6 2024 401628339… 401628… 5 5 5
#> 7 2024 401628339… 401628… 6 6 1
#> 8 2024 401628339… 401628… 7 7 1
#> 9 2024 401628339… 401628… 8 8 2
#> 10 2024 401628339… 401628… 9 9 3
#> # ℹ 146 more rows
#> # ℹ 443 more variables: pos_team <chr>, def_pos_team <chr>,
#> # pos_team_score <int>, def_pos_team_score <int>, half <fct>, period <int>,
#> # clock_minutes <dbl>, clock_seconds <dbl>, play_type <chr>, play_text <chr>,
#> # down <dbl>, distance <dbl>, yards_to_goal <dbl>, yards_gained <dbl>,
#> # EPA <dbl>, ep_before <dbl>, ep_after <dbl>, wpa <dbl>, wp_before <dbl>,
#> # wp_after <dbl>, def_wp_before <dbl>, def_wp_after <dbl>, …
# }
