pgatouR gives you access to the PGA Tour’s data through a clean set of R functions. Every function returns a tibble, so the data is immediately ready for analysis with dplyr, ggplot2, or whatever you prefer.
This vignette walks through the main use cases.
Tournament IDs
Most functions need a tournament ID. These follow the format
{tour_code}{year}{number}:
-
"R2026475"— 2026 Valspar Championship (PGA Tour) -
"R2026003"— 2026 Sentry (PGA Tour) -
"S2026003"— A Champions Tour event
You can find these from PGA Tour URLs or by using
pga_schedule() to list all tournaments for a season:
# Full season schedule with dates, purse, course, champion
pga_schedule(2026)
#> # A tibble: 48 × 15
#> tournament_id tournament_name display_date status purse ...
#> <chr> <chr> <chr> <chr> <chr> ...
#> 1 R2026006 Sony Open in Hawaii Jan 15 - 18 COMPLETED $9,100,000
#> 2 R2026002 The American Express Jan 22 - 25 COMPLETED $9,200,000
#> 3 R2026004 Farmers Insurance Op… Jan 29 - F… COMPLETED $9,600,000
#> ...The tour code prefix tells you which tour:
| Code | Tour |
|---|---|
R |
PGA Tour |
S |
PGA Tour Champions |
H |
Korn Ferry Tour |
Tracking a Live Tournament
Leaderboard
The leaderboard is the starting point for any tournament. It returns every player in the field with their scores.
lb <- pga_leaderboard("R2026475")
lb
#> # A tibble: 135 × 17
#> player_id first_name last_name display_name country position total thru
#> <chr> <chr> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 39971 Sungjae Im Sungjae Im KOR 1 -7 F*
#> 2 27064 Brandt Snedeker Brandt Snede… USA 2 -6 F
#> 3 56630 Davis Thompson Davis Thomps… USA T3 -5 F
#> 4 39997 Jordan Spieth Jordan Spieth USA T3 -5 7
#> ...For a quick snapshot of just the top 15, use
pga_current_leaders():
pga_current_leaders("R2026475")Tee Times
See who’s grouped together and when they tee off:
tt <- pga_tee_times("R2026475")
tt
#> # A tibble: 270 × 12
#> round_number tee_time start_tee display_name group_number
#> <int> <dttm> <int> <chr> <int>
#> 1 1 2026-03-19 07:35:00 1 Alex Smalley 1
#> 2 1 2026-03-19 07:35:00 1 Justin Lower 1
#> 3 1 2026-03-19 07:35:00 1 Max McGreevy 1
#> ...Tee times are returned as proper POSIXct timestamps in
the tournament’s local timezone.
Tournament Metadata
Get the full picture — name, location, dates, format, current weather:
t <- pga_tournaments("R2026475")
t$tournament_name
#> "Valspar Championship"
t$weather_condition
#> "DAY_PARTLY_CLOUDY"
t$weather_temp_f
#> "73°F"The courses column is a list-column containing a tibble
of course details for each tournament.
Broadcast Schedule
See what’s live and what’s coming up:
pga_coverage("R2026475")
#> # A tibble: 16 × 7
#> coverage_type stream_title round_number live_status
#> <chr> <chr> <int> <chr>
#> 1 BroadcastFullTelecast Round 1 - Broadcast 1 LIVE
#> 2 BroadcastFullTelecast Main Feed 2 UPCOMING
#> 3 BroadcastFeaturedGroup Marquee Group - S. Ry… 2 UPCOMING
#> ...Odds
odds <- pga_odds("R2026475")
odds
#> # A tibble: 135 × 5
#> player_id odds odds_sort odds_direction option_id
#> <chr> <chr> <dbl> <chr> <chr>
#> 1 34046 +650 7.5 UP 18776
#> 2 36689 +6000 61 DOWN 18776
#> ...Player Deep Dive
Scorecard
Get a player’s hole-by-hole results. Each row is one hole in one round:
sc <- pga_scorecard("R2026475", "39971")
sc
#> # A tibble: 18 × 11
#> round_number hole_number par score status yardage round_score
#> <int> <int> <int> <chr> <chr> <int> <chr>
#> 1 1 10 4 3 BIRDIE 437 -1
#> 2 1 11 5 3 EAGLE 561 -3
#> 3 1 12 4 3 BIRDIE 370 -4
#> ...The status column gives you the result on each hole
(EAGLE, BIRDIE, PAR, BOGEY, etc.), and round_score is the
running score relative to par.
Shot-Level Tracking
This is the most granular data available — every shot a player hits, with distances, play-by-play descriptions, and coordinate data for visualization:
shots <- pga_shot_details("R2026475", "39971", round = 1)
shots
#> # A tibble: 65 × 15+
#> hole_number stroke_number play_by_play distance
#> <int> <int> <chr> <chr>
#> 1 10 1 303 yds to left tree outline, 136… 303 yds
#> 2 10 2 134 yds to left green, 10 ft 2 in… 134 yds
#> 3 10 3 In the hole 10 ft 2 in.
#> ...The response includes coordinate columns (prefixed with
leftToRightCoords and bottomToTopCoords)
containing x, y, tourcastX,
tourcastY, and tourcastZ values for both the
from and to positions of each stroke. These
can be used to plot shot patterns on a course map.
Video Highlights
Get video clips for a specific player in a tournament:
pga_videos(player_ids = "39971", tournament_id = "475")
#> # A tibble: 18 × 17
#> title duration_secs hole_number pub_date
#> <chr> <int> <chr> <dttm>
#> 1 Sungjae Im hits 117-yard approach to… 25 5 2026-03-19 16:42:12
#> 2 Sungjae Im sinks 44-foot birdie putt… 16 3 2026-03-19 16:05:19
#> ...Note: pga_videos() takes the numeric tournament ID
("475") without the tour code prefix, while most other
functions use the full ID ("R2026475").
For shot-by-shot video clips (one clip per stroke in a round):
pga_tourcast_videos("R2026475", "39971", round = 1)Player Profiles
The player profile endpoints give you a deep dive on any player — all from REST endpoints that don’t require GraphQL.
Profile Overview
profile <- pga_player_profile("52955") # Ludvig Aberg
profile$first_name # "Ludvig"
profile$country # "Sweden"
profile$age # "26"
profile$college # "Texas Tech University"
# Career highlights: wins, FedExCup rank, world rank
profile$highlights
#> # A tibble: 6 × 3
#> title value subtitle
#> <chr> <chr> <chr>
#> 1 PGA TOUR Wins 2 NA
#> 2 Wins (2026) 0 NA
#> 3 FedExCup Standings 11 PTS: 685
#> 4 World Rank (OWGR) 17 NA
#> ...
# Quick overview stats (career, season, bio, performance)
profile$overviewPlayer Stats (131 Stats in One Call)
This is one of the most powerful endpoints — a player’s full stat profile with ranks:
stats <- pga_player_stats("52955")
#> # A tibble: 131 × 11
#> stat_id title rank value category
#> <chr> <chr> <chr> <chr> <chr>
#> 1 02675 SG: Total 13 1.245 STROKES_GAINED, SCORING
#> 2 02674 SG: Tee-to-Green 12 1.150 STROKES_GAINED, DRIVING
#> 3 02567 SG: Off-the-Tee 29 0.460 STROKES_GAINED, DRIVING
#> 4 02568 SG: Approach the Gr… 23 0.508 STROKES_GAINED, APPROACH
#> ...Tournament Results
results <- pga_player_results("52955")
results[, c("tournament", "pos", "total", "to_par", "winnings")]
#> # A tibble: 6 × 5
#> tournament pos total to_par winnings
#> <chr> <chr> <chr> <chr> <chr>
#> 1 The American Express W/D 135 -9 $-
#> 2 Farmers Insurance Open CUT 150 +6 $-
#> 3 AT&T Pebble Beach Pro-Am T37 277 -11 $78,375
#> 4 The Genesis Invitational T20 275 -9 $259,500
#> 5 Arnold Palmer Invitational T3 276 -12 $1,200,000
#> 6 THE PLAYERS Championship T5 279 -9 $925,000Career, Bio, and Tournament Status
# Career achievements
pga_player_career("52955")
# Biographical text and amateur highlights
bio <- pga_player_bio("52955")
bio$text # Character vector of bio paragraphs
length(bio$amateur_highlights) # 19 amateur achievements
# Is a player in the current tournament?
pga_player_tournament_status("39971") # Sungjae Im
#> # A tibble: 1 × 11
#> player_id tournament_name position thru score total
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 39971 Valspar Championship 1 F -7 -7Statistics
Pulling Stats
pgatouR gives you access to 300+ PGA Tour statistics going back to 2004. Every stat has a unique ID:
# Strokes Gained: Total
sg <- pga_stats("02675")
sg
#> # A tibble: 195 × 7+
#> rank player_name country ...
#> <int> <chr> <chr> ...
#> 1 1 Jacob Bridgeman United States ...
#> 2 2 Jake Knapp United States ...
#> 3 3 Scottie Scheffler United States ...
#> ...The result includes metadata as attributes:
Finding Stat IDs
The package includes a bundled stat_ids dataset with all
340 known stat IDs:
# All strokes gained stats
stat_ids[stat_ids$category == "Strokes Gained", ]
#> # A tibble: 6 × 4
#> stat_id stat_name category subcategory
#> <chr> <chr> <chr> <chr>
#> 1 02675 SG: Total Strokes Gained Strokes Gained Leaders
#> 2 02674 SG: Tee-to-Green Strokes Gained Strokes Gained Leaders
#> 3 02567 SG: Off-the-Tee Strokes Gained Strokes Gained Leaders
#> 4 02568 SG: Approach the Green Strokes Gained Strokes Gained Leaders
#> 5 02569 SG: Around-the-Green Strokes Gained Strokes Gained Leaders
#> 6 02564 SG: Putting Strokes Gained Strokes Gained Leaders
# Search by name
stat_ids[grep("Driving Distance", stat_ids$stat_name), ]
# See all categories
unique(stat_ids$category)
#> "Strokes Gained" "Off The Tee" "Approach the Green"
#> "Around the Green" "Putting" "Scoring"
#> "Streaks" "Money/Finishes" "Points/Rankings"Common Stat IDs
Here are the ones you’ll probably use most:
| Stat ID | Stat |
|---|---|
02675 |
SG: Total |
02674 |
SG: Tee-to-Green |
02567 |
SG: Off-the-Tee |
02568 |
SG: Approach the Green |
02569 |
SG: Around-the-Green |
02564 |
SG: Putting |
101 |
Driving Distance |
102 |
Driving Accuracy Percentage |
103 |
Greens in Regulation Percentage |
130 |
Scrambling |
104 |
Putting Average |
120 |
Scoring Average (Adjusted) |
Season Schedule
Get the full season schedule with dates, purse, course info, champions, and FedExCup points:
schedule <- pga_schedule(2026)
schedule
#> # A tibble: 48 × 15
#> tournament_id tournament_name display_date status purse
#> <chr> <chr> <chr> <chr> <chr>
#> 1 R2026006 Sony Open in Hawaii Jan 15 - 18 COMPLETED $9,100,000
#> 2 R2026002 The American Express Jan 22 - 25 COMPLETED $9,200,000
#> ...
# See champions and earnings for completed events
schedule[schedule$status == "COMPLETED",
c("tournament_name", "champion", "champion_earnings")]
# Use tournament IDs with other functions
pga_leaderboard("R2026006")
# Past seasons work too
pga_schedule(2025)FedExCup Standings
fc <- pga_fedex_cup(2026)
fc
#> # A tibble: 192 × 14
#> display_name this_week_rank projected_points ...
#> <chr> <chr> <chr> ...
#> 1 Jacob Bridgeman 1 1,403.981 ...
#> 2 Cameron Young 2 1,323.014 ...
#> 3 Akshay Bhatia 3 1,224.306 ...
#> ...Player Directory
The full player directory has 2,400+ players across all tours:
players <- pga_players("R")
nrow(players)
#> 2486
# Filter to active players
active <- players[players$is_active, ]
nrow(active)
# Other tours
champions <- pga_players("S")
korn_ferry <- pga_players("H")News
# Latest articles
news <- pga_news(limit = 10)
news[, c("headline", "publish_date", "franchise_display_name")]
# See available categories
pga_news_franchises()
#> # A tibble: 7 × 2
#> franchise franchise_label
#> <chr> <chr>
#> 1 latest Latest
#> 2 power-rankings Power Rankings
#> 3 expert-picks Expert Picks
#> 4 equipment-report Equipment
#> 5 tour-insider Tour Insider
#> 6 needtoknow Need to Know
#> 7 daily-wrapup Daily Wrap Up
# Filter by category
pga_news(franchises = "power-rankings", limit = 5)Tips
- Rate limiting is built in at 10 requests/second. You don’t need to add delays.
- Compressed payloads (leaderboards, scorecards, tee times, shot details, odds) are decompressed automatically. You always get a clean tibble.
- Error messages use the cli package and will tell you exactly what went wrong (bad tournament ID, API errors, etc.).
-
stat_idsis lazy-loaded — just typestat_idsto access it, nodata()call needed. - The
@returnsection in each function’s help page (?pga_leaderboard) describes the exact columns you’ll get back.