
Fast, tidy Pro Golf data in R
golfastr provides easy access to professional golf data
from ESPN with functions to get leaderboards and hole-by-hole scores in
tidy data formats ready for analysis. Supports PGA
Tour, LIV Golf, LPGA,
DP World Tour, and Champions Tour.
# Install from CRAN
install.packages("golfastr")
# Or install development version from GitHub
# install.packages("pak")
pak::pak("array-carpenter/golfastr")| Tour | Code | Example |
|---|---|---|
| PGA Tour | "pga" |
load_schedule(2026, tour = "pga") |
| LIV Golf | "liv" |
load_schedule(2026, tour = "liv") |
| LPGA | "lpga" |
load_schedule(2026, tour = "lpga") |
| DP World Tour | "euro" |
load_schedule(2026, tour = "euro") |
| Champions Tour | "champions" |
load_schedule(2026, tour = "champions") |
All functions accept the tour parameter. Default is
"pga".
library(golfastr)
# Get the PGA tournament schedule
schedule <- load_schedule(2026)
# Load a tournament leaderboard
sony <- load_leaderboard(2026, "Sony")
# Get hole-by-hole scoring for top 10
holes <- load_holes(2026, "Sony", top_n = 10)
# LIV Golf works the same way
liv_schedule <- load_schedule(2026, tour = "liv")
adelaide <- load_leaderboard(2026, "Adelaide", tour = "liv")Completed PGA Tour tournaments are served from pre-built data files hosted on GitHub releases, updated daily. This makes loading a full season near-instant and provides hole-by-hole data for the full field, not just the top finishers:
# Loads from hosted data - seconds, not minutes
season <- load_leaderboard(2025)
# Full-field hole-by-hole scoring
holes <- load_holes(2025, "Masters")
# Bypass hosted data for in-progress tournaments
live_lb <- load_leaderboard(2026, "Travelers", live = TRUE)If the hosted data is unavailable (or a tournament is still in progress), both functions automatically fall back to the live ESPN API.
Pre-built PGA Tour strokes gained data ships with the package:
# Latest strokes gained (downloads if needed; bundled data is the fallback)
sg <- load_strokes_gained()
# One player in one season, no network
load_strokes_gained("Scheffler", use_bundled = TRUE, year = 2026)Returns per-round averages for all six SG categories:
| Field | Description |
|---|---|
| sg_putt | Strokes Gained: Putting |
| sg_arg | Strokes Gained: Around the Green |
| sg_app | Strokes Gained: Approach the Green |
| sg_ott | Strokes Gained: Off the Tee |
| sg_t2g | Strokes Gained: Tee to Green |
| sg_total | Strokes Gained: Total |
Every PGA Tour stroke since 2012, plus tee times and course setup by round:
shots <- load_shots(2026, "Masters")
tee_times <- load_tee_times(2026, "Masters")
setup <- load_course_setup(2026, "Masters")
# Strokes gained for every shot, then totals by round
sg <- calculate_strokes_gained(shots)
summarise_strokes_gained(sg, by = "round")load_holes() files carry win_prob,
win_prob_before, wpa and
skill_rating for every PGA Tour stroke play event since
2004. add_win_probability() computes them for an event in
progress.
# ESPN to PGA Tour ID crosswalk
load_player_ids("Scheffler")
load_event_ids(2026)
# Logo image URLs for tours, tournaments, courses and clubs
load_logos("club", "Augusta")
# What's in the bag: equipment by club, and switches
load_witb("Scheffler", year = 2026, slot = "putter")
# Build or refresh a local SQLite database
update_db(dbdir = "~/golf")# Get schedule for a season
schedule <- load_schedule(2026)
# LIV Golf schedule
liv <- load_schedule(2026, tour = "liv")
# Returns: event_id, tournament_name, start_date, end_date# Load by tournament name (partial match)
masters <- load_leaderboard(2026, "Masters")
phoenix <- load_leaderboard(2026, "Phoenix")
# Load by event ID
lb <- load_leaderboard(2026, "401703504")
# Load all tournaments for the year
all_lb <- load_leaderboard(2026)
# LIV Golf leaderboard
liv_lb <- load_leaderboard(2026, "Adelaide", tour = "liv")# Get scorecards for top finishers
holes <- load_holes(2026, "Sony", top_n = 10)
# Returns: player_id, player_name, round, hole, par, score, score_type# Get players from recent tournaments
players <- load_players(2026)| Field | Description |
|---|---|
| position | Final standing |
| player_id | ESPN athlete ID |
| player_name | Player display name |
| total_score | Total strokes |
| score_to_par | Score vs par as text (e.g., “-11”, "E" for even
par) |
| status | Player status |
| Field | Description |
|---|---|
| position | Final leaderboard standing |
| round | Round number (1-4) |
| hole | Hole number (1-18) |
| par | Par for hole |
| score | Strokes on hole |
| score_type | BIRDIE, PAR, BOGEY, EAGLE, etc. |
Store data locally for faster repeated access:
# Save to RDS (native R format)
save_to_rds(leaderboard_data, file_path = "golf_data.rds")
# Load from RDS
data <- load_from_rds(file_path = "golf_data.rds")
# Or use Parquet for cross-language compatibility (requires arrow)
save_to_parquet(leaderboard_data, file_path = "golf_data.parquet")
data <- load_from_parquet(file_path = "golf_data.parquet")
# Auto-detect format with load_data()
data <- load_data("golf_data.rds")
data <- load_data("golf_data.parquet", tournament = "Masters")# Incrementally fetch all tournaments for a season
build_season(2025, file_path = "pga_2025.rds")
# Check progress
check_season(2025, file_path = "pga_2025.rds")Pass a tibble from load_leaderboard(), or a file saved
with save_to_rds():
season <- load_leaderboard(2025)
player_summary("Scheffler", data = season)
compare_players(c("Scheffler", "McIlroy"), data = season)
win_leaders(data = season)
scoring_avg_leaders(data = season)Tournament data is sourced from ESPN’s Golf API (https://www.espn.com/golf/). Strokes gained data is sourced from the PGA Tour (https://www.pgatour.com/stats).
MIT