Skip to content

baseballr

2.0.0Package record

Acquiring and Analyzing Baseball Data

0packages depend
33.9Kdownloads / year
3.9%test coverage
8/13flavors OK

Overview

About

Provides numerous utilities for acquiring and analyzing baseball data from online sources such as 'Baseball Reference' https://www.baseball-reference.com/, 'FanGraphs' https://www.fangraphs.com/, and the 'MLB Stats' API https://www.mlb.com/.

Changes in 2.0.0

New features

  • statcast_search() no longer renames Savant CSV columns by position. Savant's own header row is trusted as-is (its names are already the canonical identifiers), so a column inserted mid-export can never again silently shift every downstream value — the "n_thruorder_pitcher showing AGE" failure class (#337, #354, #371, #390, #408, #416). Genuinely new Savant columns now arrive under their own names with an informational message.
  • statcast_search_minors() and statcast_search_wbc() — the minor-league and World Baseball Classic Statcast searches (separate Savant routes, same columns) (#339, #395).
  • statcast_pitch_colors() — Baseball Savant's pitch-type color palette as a tibble for consistent pitch charts (#356).
Show all changes in 2.0.0Hide the rest of the changes in 2.0.0
  • Eleven load_mlb_*() loaders for the pre-computed MLB model datasets on the sportsdataverse-data releases (expected stats/HR, batter projections, Stuff+/Command+/xERA, OAA, catcher framing, RE24 matrix, WE table, WPA), mirroring sportsdataverse-py's loader surface.
  • mlb_stats() and mlb_teams_stats() gain sit_codes for situational splits via stat_type = "statSplits" (vs L/R, home/away, RISP, ...) (#278, #304, #383). Note the upstream leaders endpoints ignore sitCodes.
  • fg_projections() — FanGraphs player projections (Steamer, ZiPS, ATC, THE BAT and rest-of-season variants; batting and pitching) (#182).
  • ggpitchzone() — pitch-location plot from the catcher's perspective with the strike zone overlaid, colored with the Savant pitch-type palette (#260).

Bug fixes

  • mlb_pbp() no longer back-fills the at-bat-level matchup.postOn* / matchup.splits.menOnBase columns across at-bat and half-inning boundaries — bases-empty plays previously inherited baserunner ids from the next at-bat (#263). This corrects long-standing output.
  • mlb_pbp()'s count.balls.start / count.strikes.start are now true pre-pitch counts (the previous event's post-pitch count within the at-bat, 0-0 before the first pitch). They previously carried the post-pitch counts under a pre-pitch name (#131, #252).
  • mlb_game_info() degrades gracefully for games that have not started: venue/weather/status return with NA attendance and first-pitch fields instead of erroring (#363).
  • mlb_pbp() works for pre-2010 games again: play events are tied to their at-bat by position instead of an endTime join the older feeds don't carry (#233). Modern-game output is unchanged.
  • mlb_pbp(add_base_state = TRUE) appends per-event pre-pitch base-occupancy columns (pre_on_1b / pre_on_2b / pre_on_3b, runner MLBAM ids) reconstructed from the feed's runner-movement records, including the extra-innings automatic runner. Validated pitch-for-pitch against Statcast's own on_1b/2b/3b columns: 4,388/4,388 states agree across a full 15-game slate (#276).
  • bref_standings_on_date() supports every divisional era: pre-1969 league-only standings, 1969-1993 East/West, and 1994+ East/Central/West. Tables are named from the page's own section headings, and requesting a division that did not exist for the date errors with the era's available options (#253).
  • mlb_rosters() documents the mlb_teams() team-id lookup (#376).
  • Added read-only **Fox Sports "Bifrost"** MLB wrappers (fox_mlb_*()) over api.foxsports.com/bifrost/v1/mlb/*: fox_mlb_team_roster(), fox_mlb_team_stats(), fox_mlb_team_gamelog(), fox_mlb_standings(), fox_mlb_league_leaders(), and fox_mlb_odds(). They flatten Fox's layout-oriented JSON (sections → tables → rows → cells) into tidy baseballr_data tibbles. Fox does not expose MLB play-by-play or boxscore via event/{id}/data, so those are intentionally omitted. Parallels the cfbfastR / hoopR / fastRhockey / sportsdataverse-py fox_* families; reverse-engineering notes + an OpenAPI 3.1 spec live in the sdv-internal-refs repo.
  • Added a full **ESPN MLB** wrapper family (espn_mlb_*(), 100+ functions) mirroring the naming and structure of the sister SportsDataverse packages (hoopR espn_nba_*/espn_mbb_*, wehoop espn_wnba_*/espn_wbb_*, cfbfastR espn_cfb_*). It covers ESPN's three public hosts:
  • **Game data** (site.api.espn.com): espn_mlb_scoreboard(), espn_mlb_pbp() (pitch/at-bat level), espn_mlb_team_box(), espn_mlb_player_box(), espn_mlb_game_all(), espn_mlb_game_rosters(), espn_mlb_teams(), espn_mlb_team_current_roster(), espn_mlb_standings(), espn_mlb_betting(). Baseball-specific extractors with no basketball analogue: espn_mlb_game_probables() (probable / announced starting pitchers) and espn_mlb_game_info() (venue, attendance, game duration, and the umpire crew).
  • **Reference data** (sports.core.api.espn.com): athletes, coaches, seasons, franchises, draft, futures, leaders, positions, powerindex, events, plays, per-competitor game detail, and the season_* family.
  • **Athlete data** (site.web.api.espn.com): espn_mlb_player_overview(), espn_mlb_player_gamelog(), espn_mlb_player_splits(), espn_mlb_player_stats(), espn_mlb_team_stats().

Box scores are returned as wide, clean-named baseballr_data tibbles (one row per team for espn_mlb_team_box(); one row per athlete-side, tagged stat_group, for espn_mlb_player_box()), with the baseball batting/pitching/fielding stat groups parsed natively. ESPN requests share an internal httr2 layer (.retry_request()) that honours options(baseballr.proxy = ...). Live tests are gated behind ESPN_MLB_TESTS=1 (skip_espn_test()).

  • Added a full **ESPN College Baseball** wrapper family (espn_college_baseball_*(), 70 functions) covering ESPN's NCAA college-baseball endpoints (sport baseball, league college-baseball). They are thin twins of the espn_mlb_*() family over the same league-parameterized helpers, so return shapes match their MLB counterparts. Coverage includes game data (espn_college_baseball_scoreboard(), espn_college_baseball_pbp(), espn_college_baseball_team_box(), espn_college_baseball_player_box(), espn_college_baseball_game_all(), espn_college_baseball_game_rosters()), teams / standings / rankings / news / conferences, the team-detail and athlete families, the core-v2 reference graph (seasons, season types, groups, venues, coaches, calendar), per-competitor game sub-resources, and tournaments (the College World Series is an ESPN tournament). Endpoints ESPN does not serve for college baseball (injuries, win-probability, betting futures/odds, athlete contracts, transactions, draft, depth charts) are intentionally omitted. A new helper most_recent_college_baseball_season() supplies the season default. Endpoint/return catalog and captured sample bodies live in the sdv-internal-refs repo.

Bug fixes

  • bref_standings_on_date(), bref_daily_batter(), bref_daily_pitcher(), and bref_team_results() now retry on HTTP 429 (Too Many Requests) from Baseball Reference with exponential backoff (up to 3 tries) via a new bref_read_html() helper that wraps httr2::req_retry(). Previously, a 429 was swallowed by the tryCatch into a misleading "Invalid arguments or no data available" message, leaving the returned data as NULL (#344). Error handlers also now print the original error message for diagnosis.
  • The NCAA family (ncaa_schedule_info(), ncaa_roster(), ncaa_teams(), ncaa_team_player_stats(), ncaa_pbp(), ncaa_game_logs(), ncaa_lineups(), ncaa_park_factor()) can fetch stats.ncaa.org again — clearing the new Akamai 403 / soft bm-verify block — after the site simultaneously (a) tightened its Akamai bot protection and (b) redesigned its pages (#410). Each affected scraper was also updated for the new page layout (details below); clearing the block needs the optional chromote + Google Chrome browser fallback.
  • **Akamai bypass.** stats.ncaa.org now blocks every httr2/curl request — a hard 403 or a soft HTTP-200 bm-verify interstitial — on the client's TLS/sensor fingerprint, so no header set can clear it, and a *vanilla* headless Chrome is blocked too (Akamai fingerprints the HeadlessChrome user-agent and the navigator.webdriver flag). request_with_proxy() now falls back to a **stealth headless-Chrome** fetch (real Chrome user-agent, navigator.webdriver hidden) via the optional chromote package, wrapping the rendered HTML in a synthetic response so every caller is unaffected. The browser session is cached and reused. chromote + Google Chrome are an optional (Suggests) dependency; when absent the scrapers emit a clear install message. The fast httr2 path is used whenever Akamai allows it and resumes automatically if the edge relaxes.
  • **Page-redesign parsers.** Each scraper was updated for the new layout: ncaa_schedule_info() selects the schedule table by its header columns (it moved out of <fieldset> into a plain <table>); ncaa_team_player_stats() reads the migrated /teams/{season_team_id}/season_to_date_stats #stat_grid; ncaa_pbp() follows the "Play By Play" tab and parses the table.table inning tables; ncaa_game_logs() reads the /players/{id} #game_log / #career_totals grids; and ncaa_lineups() derives the batting order from the individual_stats box scores.
  • ncaa_game_logs() and ncaa_team_player_stats() gain **type = "fielding"** support (previously only "batting" / "pitching").
  • edge_frequency(df, group = ...) now groups by the column named in the group argument. It previously grouped by a literal column named group (.data$group), so passing a group (e.g. "pitcher") errored unless the data happened to have a group column and otherwise ignored the argument.
  • statcast_search() now assigns Baseball Savant's columns length-tolerantly and recognizes the newest Savant column definitions, so columns added to (or reordered in) the CSV export no longer break the function with a "can't assign N names to an M column data.table" error (#337, #354, #371, #390).
  • statcast_search() now recognizes Baseball Savant's miss_distance column (the bat-tracking swing-and-miss distance). Savant inserted it *mid-frame* — between swing_length and estimated_slg_using_speedangle, not as a trailing column — so the length-tolerant positional rename labeled it (and every column after it) one position off: miss_distance dropped out and the trailing bat-tracking columns were silently mislabeled. miss_distance is now in the canonical column list at its real position, so it comes through under its own name and the columns after it line up again (#408).
  • statcast_search() (via process_statcast_payload()) now normalizes blank character fields to NA. Baseball Savant exports empty fields as empty strings, so character columns such as events, des, and description were coming back as "" rather than NA (numeric columns were unaffected). This broke downstream helpers that treat missing categorical values as NA -- e.g. metrics_linear_weights_savant() was not folding balls/strikes into the events column (#275).
  • statcast_search() (via process_statcast_payload()) returns data again. Baseball Savant's CSV export ships the pitcher and fielder_2 columns twice (the trailing repeats carry identical values), and after the positional header rename those duplicate names survived into the payload. dplyr::across()/mutate() reject a frame with duplicate names (Can't transform a data frame with duplicate names), so the function now drops the duplicate-named columns (keeping the first occurrence) before any column-wise transform, class-aware for the data.table payload returned by csv_from_url().
  • sptrc_team_active_payroll() and sptrc_league_payrolls() updated for Spotrac's new /payroll/_/year/<year>/ URLs and changed table schema; parsing is now resilient to column-order changes and both functions return data again (#392).
  • Wrappers that build their result inside tryCatch() now initialize the return value first, so an API error returns an empty value with a cli message instead of an object '<var>' not found error.
  • mlb_game_timecodes() no longer returns NULL; it renamed its single column by the literal name ".", which newer R versions no longer use for the coerced column, so it now renames by position.
  • mlb_homerun_derby() and mlb_homerun_derby_bracket() return data again; their column selections are now wrapped in dplyr::any_of() so the dropped num_batters column (no longer in the MLB Stats API response) no longer errors the parse.
  • The NCAA functions (ncaa_*()) work again. stats.ncaa.org moved behind Akamai, which returns HTTP 403 "Access Denied" to requests lacking modern browser headers; .ncaa_headers() now sends the current client-hint / fetch-metadata headers (sec-ch-ua, Sec-Fetch-*, Upgrade-Insecure-Requests), and ncaa_teams() / ncaa_team_player_stats() now request over https. request_with_proxy() now sleeps 5 seconds after every request (the NCAA edge aggressively rate-limits / IP-bans) and accepts a proxy argument (a URL string or a list of httr2::req_proxy() args, defaulting to getOption("baseballr.proxy")) to route and rotate requests through a proxy. (Verified: ncaa_roster(), ncaa_teams().)
  • ncaa_teams() no longer returns NA ids. stats.ncaa.org migrated team links from the legacy /team/{team_id}/{season_id} pair to a single modern /teams/{season_team_id} resource, so the previous str_extract("(\\d+)/(\\d+)") matched nothing and team_id/season_id came back NA. The function now extracts ids from whichever url shape is present (legacy rows populate team_id/season_id; modern rows populate the new season_team_id column) so the id columns are never all-NA when the site flips formats.
  • ncaa_team_player_stats() builds its request correctly and fails gracefully (#379). The batting branch previously passed id twice and never sent year_stat_category_id, so it could not return batting stats; both branches now key /team/{team_id}/stats on the season id and the per-type stat-category id (batting_id / pitching_id) from the season-id lookup. The stats endpoint is currently gated behind Akamai's bm-verify interstitial challenge (verified: the URL and ids are correct and work in a real browser, but a static request — even with valid session cookies carried from the roster page — is served the challenge, not the data). When challenged, the function now emits an informative cli warning and returns an empty tibble (via the new internal .ncaa_is_interstitial() guard) instead of silently scraping a table-less page.
  • fg_batter_leaders() returns data for FanGraphs' handedness-split leaderboards (month = 13 for vs LHP, month = 14 for vs RHP) instead of NULL (#323). The split boards return a much narrower column projection (~83-97 columns) than the full board (~475), and the function's rename()/select() referenced columns absent from that projection, so the parse errored and was swallowed to NULL. The leading rename and column selection now use dplyr::any_of(), so a column missing from a narrower split is skipped rather than failing the whole parse. The same any_of() hardening was applied to fg_pitcher_leaders() and fg_fielder_leaders(), which shared the strict select() / rename() pattern (verified live: fg_pitcher_leaders(month = "13") returns data).
  • fg_batter_leaders(), fg_pitcher_leaders(), fg_fielder_leaders(), fg_team_batter(), fg_team_pitcher(), and fg_team_fielder() now honor startdate/enddate out of the box (#326). FanGraphs only applies a custom date range when month = "1000", so supplying dates with the default month = "0" silently returned the full-season board. month is now set to "1000" automatically when a date range is requested without an explicit month, and the month documentation covers the special values ("13" vs LHP, "14" vs RHP, "1000" date range). Verified live: a 2023-03-26..2023-04-26 pull returns the date-ranged board (max 118 PA) instead of the full season (max 753 PA).
  • ncaa_park_factor() no longer errors mid-pipeline when the NCAA schedule is unavailable (#302). ncaa_schedule_info() can come back empty -- stats.ncaa.org rate-limits / blocks automated requests and its sche

The NEWS section is longer than shown here.

From NEWS.md in the release

Install

Health

CRAN checks
5NOTE8OK
Check messages
NOTEdependencies in R coder-devel-linux-x86_64-debian-clang, r-devel-linux-x86_64-debian-gcc, r-devel-linux-x86_64-fedora-clang, r-devel-linux-x86_64-fedora-gcc, r-devel-windows-x86_64

r-devel-linux-x86_64-debian-clang, r-devel-linux-x86_64-debian-gcc, r-devel-linux-x86_64-fedora-clang, r-devel-linux-x86_64-fedora-gcc

Base package in Suggests/Enhances imported in NAMESPACE:
  ‘stats’

r-devel-windows-x86_64

Base package in Suggests/Enhances imported in NAMESPACE:
  'stats'

CRAN check page ↗

Slowest check: 10.3 min · r-oldrel-windows-x86_64
Status changes
  • NOTE8 OK, 5 NOTENow
  • NOTE11 OK, 2 NOTE2026-09-12
  • OK13 OK2026-09-04
  • NOTE13 NOTE2026-06-09
  • ERROR12 NOTE, 1 ERROR2026-06-08
  • NOTE3 OK, 11 NOTE2026-03-10
Advisories

none known

Documentation

In release 2.0.0
READMEYes · 1,639 wordsVignettes· In the repository, left out of the releaseNEWSYes · 67% structured · mentions 2.0.0Citation fileNoWebsiteYes · pkgdown
Help pages
402 (17 internal)includes a package overview page
Examples that run
1%3 run in a plain check · 308 only when donttest examples are enabled (R CMD check --as-cran)
Exported functions with examples
86%
Documented parameters
100%
Return-value docs
100%
Help pages citing references
0%

Downloads

33.9K
CRAN downloads in the past year
Rank #1,886 · ~93/day · ~2.8K/mo
Daily download trend is not available in this view yet.
1.7K30 days
10.3K90 days
33.9K1 year
Compare downloads with other packages →
Also on1.4K r2u47 autocran79 c2d4u

Repository

433Stars
110Forks
7Open issues
1Open PRs
5Releases
753Commits
25Commit identities
baseballsabermetricsrstatcastpitchfx
753 commits · Last activity 2026-10-09 · -0.2% stars, 30d
Responsiveness
How these are sampled
PRs merged (all time)
81%
Median days to close, 50 most recently updated closed issues
835
Median days to close, 50 most recently updated closed PRs
0

Stars over time

2025-10-17 · 3912026-10-06 · 433
PracticesBeta

15 development-tooling and community-health practices detected across 7 families in the upstream repository

Checks run against github.com/billpetti/baseballr on 2026-10-18.

Continuous integration (2)
GitHub ActionsR CMD check workflow

The R CMD check workflow runs on Linux, macOS and Windows, and on R-devel

Governance and community (4)
Code of conductContributing guideIssue templatesPR template
Docs source (4)
README.Rmdpkgdown

GitHub Pages site last deployed 2026-10-09 · The repository has a site deploy workflow

pkgdown site last built 2026-10-09

Reproducibility and dev environment (1)
data-raw/
Show all practicesHide 3 practices
CRAN release process (2)
cran-comments.mdCRAN-SUBMISSION
Lint, format, editor (1)
RStudio project
Git structural (1)
.gitattributes
How this is detected·Detection ruleset v3 (2026-09-26)
AI coding toolsBeta

This repository shows signs of AI coding tools.

  • Claude Codefirst seen 2026-06-08
    Seen in the repository: CLAUDE.md
    Named in .Rbuildignore: .claude, CLAUDE.md
  • GitHub Copilotfirst seen 2026-06-08
    Seen in the repository: .github/copilot-instructions.md
From outside contributors
  • Claude Code
    Pull requests: 1 from outside contributors, first on 2026-06-04, 1 from forks
How this is detected·Detection ruleset v4 (updated 2026-09-27)

Dependencies

Declared dependencies
37 external dependencies (excludes base and recommended)

Installing baseballr adds 48 packages (27 with compiled code, 41 MB of source tarballs). Heaviest: stringi 11 MB, ggplot2 6.0 MB, data.table 5.7 MB.

Depends (1)
R >= 4.1.0
LinkingTo (0)
none
Enhances (0)
none
Reverse dependencies
0direct
0indirect

Nothing depends on this yet.

Datasets

People & History

People (7)
Author, Maintainer since 1.2.0 (2022)
Contributor
Contributor
Contributor
Contributor · added in 1.3.0
Copyright holder · added in 2.0.0
Releases (5)

Pick two releases to compare their code metrics.

  • 2.0.0Latest
    record
    2026-08-26 · current release · diff ↗ ·
    added 203 exports
    espn_college_baseball_athletes_index, espn_college_baseball_calendar, espn_college_baseball_coach, espn_college_baseball_coach_record, espn_college_baseball_coach_season, espn_college_baseball_coaches, espn_college_baseball_conferences, espn_college_baseball_game_all, espn_college_baseball_game_broadcasts, espn_college_baseball_game_official_detail, espn_college_baseball_game_officials, espn_college_baseball_game_play, espn_college_baseball_game_play_personnel, espn_college_baseball_game_player_box, espn_college_baseball_game_rosters, espn_college_baseball_game_situation, espn_college_baseball_game_team_leaders, espn_college_baseball_game_team_linescores, espn_college_baseball_game_team_records, espn_college_baseball_game_team_roster, espn_college_baseball_game_team_roster_entry, espn_college_baseball_game_team_score, espn_college_baseball_game_team_statistics, espn_college_baseball_leaders, espn_college_baseball_news, espn_college_baseball_pbp, espn_college_baseball_player_awards, espn_college_baseball_player_box, espn_college_baseball_player_career_stats, espn_college_baseball_player_eventlog and 173 more
  • R
    R 4.6.0 released · 2026-04-24
  • R
    R 4.5.0 released · 2025-04-11
  • R
    R 4.4.0 released · 2024-04-24
  • unarchivedReturned to CRAN
    2024-01-16
  • 1.6.0
    record
    2024-01-16 · diff ↗ ·
    added 7 exports
    chadwick_find_lib, chadwick_is_installed, chadwick_ld_library_path, chadwick_path, chadwick_set_ld_library_path, fg_fielder_leaders, fg_team_fielder
  • archivedRemoved from CRAN
    2023-10-15
    repeated policy violation On Internet access
  • R
    R 4.3.0 released · 2023-04-21
  • unarchivedReturned to CRAN
    2023-03-21
  • 1.5.0
    record
    2023-03-21 · diff ↗ ·
    added 8 exports
    load_ncaa_baseball_pbp, load_ncaa_baseball_schedule, load_ncaa_baseball_season_ids, load_ncaa_baseball_teams, ncaa_pbp, ncaa_roster, ncaa_team_player_stats, ncaa_teams
  • archivedRemoved from CRAN
    2023-02-07
    policy violation On Internet access
  • 1.3.0
    record
    2022-09-09 · diff ↗ ·
    added 4 exports
    fg_team_batter, fg_team_pitcher, sptrc_league_payrolls, sptrc_team_active_payroll
  • 1.2.0
    record
    2022-04-25
  • R
    R 4.2.0 released · 2022-04-22

Package metadata

First published
2022-04-25
Total releases
5 / 4 yrs
License
MIT + file LICENSE OSI
Minimum R
≥ 4.1.0
Bundled data
36 KB / 4 files
Download size
747 KB
Appears in task views

Cite

Cite this package

Run in R for the authors' preferred citation:

citation("baseballr")
Gilani, S., baseballr authors, Baumer, B., Dilday, B., Frey, R., Kay, C., & Petti, B. (2026). baseballr: Acquiring and Analyzing Baseball Data (Version 2.0.0) [Computer software]. https://doi.org/10.32614/CRAN.package.baseballr

This package has no citation file, so citation() returns this entry built from DESCRIPTION.

Cite the R Observatory

For a number measured here: a download total, a coverage figure, an archival date.

APA

Balamuta, J. J. (2026). R Observatory: Metrics for baseballr version 2.0.0 [Data set]. HJJB, LLC. Data release v2026-10-21. https://doi.org/10.5281/zenodo.21843040

From data release v2026-10-21, which the citation names so these numbers can be found later. More on citing and the projects behind them.

Report a problem with this page →

Privacy choices

These apply to this browser and are stored on this device only. Nothing about your choice is sent to us.

Read the privacy policy