NEWS


cfbfastR 3.0.0.9000

The development version is numbered 3.0.0.9000 so it can be told apart from the CRAN 3.0.0 release, which ships the previous model generation. Until now both builds reported 3.0.0, so packageVersion("cfbfastR") could not distinguish them — while producing different EPA/WPA for the same play. A four-component version means you are on the GitHub build. See the model note below before mixing outputs from the two.

New: user-facing model calculators

Ten calculate_*() functions score a data frame with the models cfbfastR already ships — hand them play-by-play or a row you typed to ask a hypothetical:

calculate_expected_points(pbp)
calculate_field_goal_probability(data.frame(season = 2024, yards_to_goal = 25))
calculate_fourth_down(data.frame(season = 2024, down = 4, distance = 2,
  yards_to_goal = 45, posteam_total = 52, posteam_spread = -3))

calculate_expected_points(), calculate_win_probability(), calculate_epa(), calculate_wpa(), calculate_field_goal_probability(), calculate_completion_probability(), calculate_xpass(), calculate_two_point_probability(), calculate_fourth_down() and calculate_qbr().

create_epa() and create_wpa_naive() remain as the low-level layer these wrap. They stay useful to callers who already hold booster objects — but they require you to obtain those objects from an internal loader, which is why an exported function was still unusable by an ordinary user.

Every calculator validates against the model's own published card. Each model in the cfb_model_artifacts bundle ships a <model>.card.json declaring the ordered features it was trained with, and where relevant an era_contract. Feature lists and era cutpoints are now read from those cards rather than restated in this package — the duplication that caused cfbfastR-cfb-data#70, where both cfbfastR and sportsdataverse-py kept a private copy of the era boundary, both drifted to a 2017 cut the trainer never used, and 2018-2020 scored an era off the models trained with them.

A missing column names every absent column and the model that wanted it, rather than failing inside xgboost. Play-by-play column names are normalized automatically, so a frame carrying start.TimeSecsRem is accepted as-is, and every input column is preserved so chaining two calculators is lossless.

calculate_field_goal_probability() returns fg_make_prob, not fg_prob: calculate_expected_points() uses FG for the probability the next score is a field goal, which is a different quantity from the probability a kick is made.

A committed fixture pins these against sportsdataverse-py's equivalents across all four rule eras — 20 output columns, agreeing to 1e-5.

New: CFBD 5.31.1 schedule, preview and team-overview endpoints

Fixed: ESPN play-by-play carries the pre-game line

espn_cfb_pbp(epa_wpa = TRUE) on the default v2 engine (and espn_cfb_pbp_v2()) now attaches the pre-game spread (the home team's line, negative when the home team is favoured, as on the CFBD path) and over_under. It never set them, so vegas_wp, vegas_wpa, the fourth-down decision columns (go_wp, fg_wp, punt_wp, go_boost, ...) and the two-point columns were NA on every ESPN game. The line is ported from sportsdataverse-py: the game summary's pickcenter when it lists more than one provider (games before 2024), else ESPN's core odds collection (ESPN emptied pickcenter for 2024+), preferring ESPN BET. A game with no line keeps NA rather than a default, and a failed request warns. It costs one request from 2024 on and up to two before, only when epa_wpa = TRUE. Expected points, EPA and the naive win probability are unchanged, as is the legacy engine, which computes no spread-based column.

Fixed: QBR scores the retrained model

create_qbr() now passes the QBR booster the feature columns the booster itself declares. It used a fixed 10-column list that included spread, so the 2026-09 xQBR retrain (9 features, no spread) would have made predict() fail and every exp_qbr come back NA without an error once the shared cfb_model_artifacts release carries it. Both contracts now score.

Fixed: kickoff rules before 2012

Fixed: CFBD game box score, team stats and play-by-play

Documentation: CFBD return tables

New: CFBD passing and rushing endpoint families

Ten new cfbd_*() wrappers close the last gaps against the CollegeFootballData API (spec v5.27.1), taking coverage from 74 of 84 endpoints to 84 of 84:

| function | endpoint | |---|---| | cfbd_passing_players_season() | /passing/players/season | | cfbd_passing_players_games() | /passing/players/games | | cfbd_passing_teams_season() | /passing/teams/season | | cfbd_passing_teams_games() | /passing/teams/games | | cfbd_passing_plays() | /passing/plays | | cfbd_rushing_players_season() | /rushing/players/season | | cfbd_rushing_players_games() | /rushing/players/games | | cfbd_rushing_teams_season() | /rushing/teams/season | | cfbd_rushing_teams_games() | /rushing/teams/games | | cfbd_rushing_plays() | /rushing/plays |

These carry the charting detail the older cfbd_stats_*() and cfbd_metrics_*() families do not: passing production split across seven pass locations (short and deep x left/middle/right, plus unknown), rushing production split across four run directions, and play-level frames with air yards, yards after catch, target ids, run direction and carrier attribution.

The frames are wide by construction rather than by accident. One 23-column passing production block repeats per location, and the team endpoints repeat the whole thing for offense_ and defense_, so cfbd_passing_teams_season() is 3 + 2 x (23 + 7 x 23) = 371 columns. Rather than a 371-row returns table, ?cfbd_passing documents the production block and the bucket naming scheme once and every column follows from it; ?cfbd_rushing does the same for the 24/26-column rushing block and its four directions.

Two things worth knowing before using them:

New: conference and division group loaders

Four loaders read the new cfb_groups release on sportsdataverse-data, which records conference membership as it was each season rather than back-applying today's alignment:

The loaders read the tag's parquet files, not its csv copies, so ids stay character instead of being parsed as integers.

Air yards side the catch spot by the game's own text abbreviations

The 2025+ ESPN vendor text spots the catch with each school's own abbreviation (UHM, GSO, USC for South Carolina, Sac St, BC.), which is frequently not the payload's home_team_abbreviation / away_team_abbreviation. The helper behind air_yards, air_yardsToEndzone and yards_after_catch now learns each game's abbreviations from its own "... to the ABC nn" end spots against yards_to_goal_end (majority vote per abbreviation) before falling back to the payload abbreviations, reads every observed token shape (UA 10, BC.41, Sac St10, NC ST19) and resolves a spot at the 50 without one. On 2025 new-template games this lifts in-game air-yards coverage from 72% to 90% of pass plays; every spot-phrase play now resolves. Same logic as sdv-py #418; the parity oracle was re-captured from sdv-py 9efee9f1 and extended with five affected 2025/26 games (tests/testthat/fixtures/parity/README.md).

Expected Points model now comes from the shared cfb_model_artifacts bundle

The EP model is now the XGBoost artifact published in cfb_model_artifacts — the same artifact sportsdataverse-py scores with, so both libraries agree on EPA for a given play and a retrain updates both from one publish (#138).

Decision surfaces and QBR from the same bundle

The three remaining cfb_model_artifacts artifacts are now used: the two-point, fourth-down and QBR models (#140). Unlike the model columns above, these are analytic surfaces — they build the game state that WOULD follow a choice, score it, and compare. They are ports of cfb4th and degrade to NA columns rather than failing when a model, the punt table, arrow, or a pre-game line is missing.

cfbfastR 3.0.0 (2026-08-24)

New release-dataset loaders (48 functions)

cfbfastR now loads every published CFB dataset on the sportsdataverse-data release repo, closing the gap with sportsdataverse-py's loader surface. All loaders return cfbfastR_data-tagged tibbles, accept seasons = TRUE for the full published range, and support the dbConnection/tablename database write-through.

Which play-by-play loader do I want? Nothing is deprecated — the classic functions are unchanged; the new families add sources:

This release adds a 65-function ESPN college-football API layer, expanding cfbfastR's ESPN surface from 8 wrappers to 73. The new wrappers expose ESPN's core-v2 endpoints in ESPN's own ID space — complementary to the CollegeFootballData (cfbd_*) wrappers, and the natural join partners for espn_cfb_pbp() / espn_cfb_scoreboard(). Every wrapper was verified live against the 2023, 2024, and 2025 seasons.

Naming alignment with the sportsdataverse convention (this dev cycle, never on CRAN): espn_cfb_player_statistics() is renamed to espn_cfb_player_career_stats() (the core-v2 /athletes/{id}/statistics career view, matching hoopR/wehoop/sportsdataverse-py). New espn_cfb_player_stats_v3() wraps the comprehensive web-common-v3 /athletes/{id}/stats payload (all categories, long format) — the _v3 companion to espn_cfb_player_stats() (core-v2 season statistics).

New Fox Sports API wrappers (fox_cfb_*)

A read-only Fox Sports "Bifrost" college-football layer (api.foxsports.com/bifrost/v1/cfb/*), complementary to the espn_cfb_* and cfbd_* families. Eight wrappers flatten Fox's layout-oriented JSON (sections → tables → rows → cells) into tidy cfbfastR-tagged tibbles. Reverse-engineering notes and an OpenAPI 3.1 spec live in the sdv-internal-refs repo. Verified live against the 2025 season.

New ESPN wrappers — football-specific metrics

New ESPN wrappers — players

New ESPN wrappers — teams

New ESPN wrappers — game detail

New ESPN wrappers — catalogs and season metadata

New Yahoo Sports wrappers

New CollegeFootballData wrappers

Play-by-play engine — v2 is now the default

CFBD API coverage

Audited against the CollegeFootballData OpenAPI spec (5.24.1, 74 endpoints).

15 endpoints that had no wrapper now have one: cfbd_playoffs_cfp(), cfbd_playoffs_cfp_games(), cfbd_playoffs_cfp_participants(), cfbd_conference_affiliations(), cfbd_conference_changes(), cfbd_coaches_profile(), cfbd_coaches_seasons(), cfbd_coaches_tenures(), cfbd_ratings_core(), cfbd_ratings_srs_expanded(), cfbd_teams_fbs(), cfbd_stats_player_success(), cfbd_stats_player_success_game(), cfbd_player_season_overview() and cfbd_info_usage(). Every one was exercised against the live API before being committed.

26 parameters added to existing wrappers — most importantly division on ten more functions, plus defense / offense_conference / defense_conference / conference / division on cfbd_pbp_data(), competition and round on cfbd_game_info() (College Football Playoff filtering), line_provider on cfbd_betting_lines(), conference on cfbd_play_stats_player() and recruit_type on cfbd_recruiting_position(). cfbd_conferences() previously took no arguments at all and now accepts year and division.

New validate_division() covering fbs / fcs / ii / ii/iii / iii. This validates locally because CFBD ignores an unrecognised filter value rather than rejecting it — so without it a typo silently returns every division.

Two spec parameters were deliberately not exposed after testing them: /rankings declares latest and final as booleans, but the API returns HTTP 400 for every form of both. poll is validated to "cfp", the only value it accepts.

Bug fixes

Internal changes

cfbfastR 2.2.0

cfbfastR 2.1.0

cfbfastR 2.0.0 (2025-09-09)

Breaking Changes to Loading Functions

Now upgraded to the CFBD v2 API

Special thanks are in order for our newest contributor, Brad Hill (@bradisbrad) for providing most of the v2 upgrade via his first PR to cfbfastR!! 🙌🏽 👑 🥇 Your contributions are most appreciated by the community.

Note: The free-tier API key for the CFBD v2 API has a strict 1k calls/month limit, so plan your workflows accordingly! If you receive errors mentioning r Request failed [429], you have most likely run out of API calls for the month in your membership tier.

cfbfastR 1.9.5

cfbfastR 1.9.4

cfbfastR 1.9.3

cfbfastR 1.9.2

cfbfastR 1.9.1

cfbfastR 1.9.0 (2022-06-13)

Added functions to access ESPN API:

Added functions to pull data from the data repo:

cfbfastR 1.8.0

cfbfastR 1.7.1

cfbfastR 1.7.0

cfbfastR 1.6.7

cfbfastR 1.6.6

cfbfastR 1.6.5

cfbfastR 1.6.4 (2021-10-27)

cfbfastR 1.6.3

cfbfastR 1.6.2

cfbfastR 1.6.1

cfbfastR 1.6.0

cfbfastR 1.5.2

cfbfastR 1.5.1

Minor release

cfbfastR 1.5.0

Added espn_cfb_scoreboard()

Added espn_cfb_pbp()

cfbfastR 1.4.0

Added cfbd_game_weather()

cfbfastR 1.3.3

Hotfix cfbd_game_player_stats()

cfbfastR 1.3.2

Added ID linking to cfbd_recruiting_players()

cfbfastR 1.3.0-1

Added three NFL draft functions:

cfbfastR 1.2.1

Minor release

cfbfastR 1.2.0-1

Add significant documentation to the package

ESPN/CFBD metrics function variable return standardization

cfbfastR 1.1.0

Add loading from Data Repository functionality

Add support for parallel processing and progress updates

cfbfastR 1.0.0

Function Naming Convention Change

College Football Data API Keys

The CollegeFootballData API now requires an API key, here's a quick run-down:

CFBD_API_KEY = XXXX-YOUR-API-KEY-HERE-XXXXX

Save the script and restart your RStudio session, by clicking Session (in between Plots and Build) and click Restart R (n.b. there also exists the shortcut Ctrl + Shift + F10 to restart your session). If set correctly, from then on you should be able to use any of the cfbd_ functions without any other changes.

Sys.setenv(CFBD_API_KEY = "XXXX-YOUR-API-KEY-HERE-XXXXX")