Skip to content

Raw client

PgaApi is the lower-level, object-oriented interface. Its convenience methods decode compressed responses but otherwise preserve upstream field names and response structures. Use it when you need the original nested JSON rather than normalized pandas DataFrames.

pga_tour_api.PgaApi

Client for the public data calls used by pgatour.com.

Parameters:

Name Type Description Default
api_key Optional[str]

Public frontend key. Defaults to PGA_API_KEY and then the browser key bundled with this release.

None
min_interval float

Minimum delay between requests in seconds. The default is deliberately conservative because no official limit is published.

1.0
timeout float

Per-request timeout in seconds.

30.0
user_agent str

Descriptive User-Agent sent with every request.

'pga-tour-unofficial-api/0.1'
Source code in src/pga_tour_api/raw.py
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
class PgaApi:
    """Client for the public data calls used by ``pgatour.com``.

    Parameters:
        api_key: Public frontend key. Defaults to ``PGA_API_KEY`` and then the
            browser key bundled with this release.
        min_interval: Minimum delay between requests in seconds. The default
            is deliberately conservative because no official limit is published.
        timeout: Per-request timeout in seconds.
        user_agent: Descriptive User-Agent sent with every request.
    """

    def __init__(
        self,
        api_key: Optional[str] = None,
        min_interval: float = 1.0,
        timeout: float = 30.0,
        user_agent: str = "pga-tour-unofficial-api/0.1",
    ) -> None:
        self.api_key = api_key or os.environ.get("PGA_API_KEY") or DEFAULT_BROWSER_KEY
        self.min_interval = min_interval
        self.timeout = timeout
        self.user_agent = user_agent
        self._last_request = 0.0

    def _headers(self) -> dict[str, str]:
        return {
            "Accept": "application/graphql-response+json, application/json",
            "Content-Type": "application/json",
            "Origin": "https://www.pgatour.com",
            "Referer": "https://www.pgatour.com/",
            "User-Agent": self.user_agent,
            "x-api-key": self.api_key,
            "x-pgat-platform": "web",
        }

    def _request(self, url: str, body: Optional[dict[str, Any]] = None) -> Any:
        wait = self.min_interval - (time.monotonic() - self._last_request)
        if wait > 0:
            time.sleep(wait)

        data = json.dumps(body).encode("utf-8") if body is not None else None
        request = urllib.request.Request(
            url,
            data=data,
            headers=self._headers(),
            method="POST" if body is not None else "GET",
        )

        for attempt in range(3):
            try:
                self._last_request = time.monotonic()
                with urllib.request.urlopen(request, timeout=self.timeout) as response:
                    return json.load(response)
            except urllib.error.HTTPError as exc:
                if exc.code not in {408, 429, 500, 502, 503, 504} or attempt == 2:
                    detail = exc.read(300).decode("utf-8", "replace")
                    raise PgaApiError(f"HTTP {exc.code}: {detail}") from exc
            except urllib.error.URLError as exc:
                if attempt == 2:
                    raise PgaApiError(str(exc)) from exc
            time.sleep((2**attempt) + random.random() / 4)

        raise PgaApiError("request failed after retries")

    @staticmethod
    def decompress(payload: str) -> Any:
        """Decode a ``base64(gzip(JSON))`` response payload."""
        try:
            return json.loads(gzip.decompress(base64.b64decode(payload)))
        except Exception as exc:
            raise PgaApiError(f"could not decode compressed payload: {exc}") from exc

    @staticmethod
    def _query_text(operation: str) -> str:
        try:
            return (
                resources.files("pga_tour_api.queries")
                .joinpath(f"{operation}.graphql")
                .read_text(encoding="utf-8")
            )
        except (FileNotFoundError, ModuleNotFoundError) as exc:
            raise PgaApiError(f"unknown GraphQL operation: {operation}") from exc

    def graphql(self, operation: str, variables: Optional[dict[str, Any]] = None) -> dict[str, Any]:
        """Run a bundled GraphQL operation and return its ``data`` object."""
        response = self._request(
            GRAPHQL_URL,
            {
                "operationName": operation,
                "query": self._query_text(operation),
                "variables": variables or {},
            },
        )
        if response.get("errors"):
            messages = "; ".join(
                item.get("message", "") for item in response["errors"]
            )
            raise PgaApiError(messages)
        return response.get("data", {})

    def rest(self, path: str) -> Any:
        """GET a path from ``data-api.pgatour.com``."""
        return self._request(f"{REST_URL}/{path.lstrip('/')}")

    def config(self) -> dict[str, Any]:
        """Return the frontend's current tournaments and active seasons."""
        return self._request(f"{CONFIG_URL}/web-config")

    def _root(self, operation: str, variables: dict[str, Any], root: str) -> Any:
        return self.graphql(operation, variables).get(root)

    def _compressed(self, operation: str, variables: dict[str, Any], root: str) -> Any:
        value = self._root(operation, variables, root)
        if not value or not value.get("payload"):
            return None
        return self.decompress(value["payload"])

    # Discovery and REST -------------------------------------------------

    def current_tournament(self, tour: str = "R") -> str:
        """Return the default/current tournament ID for a tour."""
        events = self.config().get("defaultTournaments", {}).get(tour, [])
        if not events:
            raise PgaApiError(f"no current tournament for tour {tour!r}")
        return events[0].get("leaderboardId") or events[0]["id"]

    def schedule(self, year: int, tour: str = "R") -> dict[str, Any]:
        """Return a season schedule."""
        return self.rest(f"schedule/{tour}/{year}")

    def players(self, tour: str = "R") -> dict[str, Any]:
        """Return the full player directory for a tour."""
        return self.rest(f"player/list/{tour}")

    def player_profile(self, player_id: str) -> dict[str, Any]:
        """Return profile overview data for a player."""
        return self.rest(f"player/profiles/{player_id}")

    def player_career(self, player_id: str) -> dict[str, Any]:
        """Return career achievements and totals for a player."""
        return self.rest(f"player/profiles/{player_id}/career")

    def player_results(self, player_id: str, season: Optional[int] = None) -> dict[str, Any]:
        """Return tournament results for a player, optionally for one season."""
        suffix = f"?season={season}" if season is not None else ""
        return self.rest(f"player/profiles/{player_id}/results{suffix}")

    def player_stats(self, player_id: str) -> dict[str, Any]:
        """Return the complete profile statistics response for a player."""
        return self.rest(f"player/profiles/{player_id}/stats")

    def player_bio(self, player_id: str) -> dict[str, Any]:
        """Return biography and amateur highlights for a player."""
        return self.rest(f"player/profiles/{player_id}/bio")

    def odds_markets(self, tournament_id: str) -> Any:
        """Return the active betting-market catalog for a tournament."""
        return self.rest(f"odds/tournament/{tournament_id}")

    def player_odds(self, tournament_id: str, player_id: str) -> Any:
        """Return active betting markets for one player."""
        return self.rest(f"odds/tournament/{tournament_id}/player/{player_id}")

    def odds_interactivity(self) -> Any:
        """Return configuration used by the odds widgets."""
        return self.rest("odds/interactivity")

    def speed_rounds(self, tour: str = "R") -> Any:
        """Return the speed-round video index for a tour."""
        return self.rest(f"content/watch/speedRounds/{tour}")

    # Tournament GraphQL -------------------------------------------------

    def leaderboard(self, tournament_id: str) -> Any:
        """Return a decoded full leaderboard."""
        return self._compressed(
            "LeaderboardCompressedV3",
            {"leaderboardCompressedV3Id": tournament_id},
            "leaderboardCompressedV3",
        )

    def current_leaders(self, tournament_id: str) -> Any:
        """Return and decode the compact current-leaders payload."""
        return self._compressed(
            "CurrentLeadersCompressed",
            {"tournamentId": tournament_id},
            "currentLeadersCompressed",
        )

    def field(self, tournament_id: str, include_withdrawn: bool = True) -> Any:
        """Return the event field, alternates, and optional withdrawals."""
        return self._root(
            "Field",
            {"fieldId": tournament_id, "includeWithdrawn": include_withdrawn, "changesOnly": False},
            "field",
        )

    def field_stats(self, tournament_id: str, stat_type: str = "CURRENT_FORM") -> Any:
        """Return current-form or course-fit data for the event field."""
        return self._root(
            "FieldStats",
            {"tournamentId": tournament_id, "fieldStatType": stat_type},
            "fieldStats",
        )

    def leaderboard_holes(self, tournament_id: str, round: Optional[int] = None) -> Any:
        """Return whole-field hole-by-hole scores for a round."""
        return self._root(
            "LeaderboardHoleByHole",
            {"tournamentId": tournament_id, "round": round},
            "leaderboardHoleByHole",
        )

    def tee_times(self, tournament_id: str) -> Any:
        """Return and decode tee groups and player assignments."""
        return self._compressed(
            "TeeTimesCompressedV2",
            {"teeTimesCompressedV2Id": tournament_id},
            "teeTimesCompressedV2",
        )

    def scorecard(self, tournament_id: str, player_id: str) -> Any:
        """Return and decode one player's hole-by-hole scorecard."""
        return self._compressed(
            "ScorecardCompressedV3",
            {"tournamentId": tournament_id, "playerId": player_id},
            "scorecardCompressedV3",
        )

    def shot_details(
        self,
        tournament_id: str,
        player_id: str,
        round: int,
        include_radar: bool = False,
    ) -> Any:
        """Return and decode shot-level tracking for one player and round."""
        return self._compressed(
            "shotDetailsV4Compressed",
            {
                "tournamentId": tournament_id,
                "playerId": player_id,
                "round": round,
                "includeRadar": include_radar,
            },
            "shotDetailsV4Compressed",
        )

    def odds(self, tournament_id: str) -> Any:
        """Return and decode tournament winner odds."""
        return self._compressed(
            "oddsToWinCompressed",
            {"tournamentId": tournament_id},
            "oddsToWinCompressed",
        )

    def coverage(self, tournament_id: str) -> Any:
        """Return television and streaming coverage windows."""
        return self._root("Coverage", {"tournamentId": tournament_id}, "coverage")

    def weather(self, tournament_id: str) -> Any:
        """Return hourly and daily tournament weather forecasts."""
        return self._root("Weather", {"tournamentId": tournament_id}, "weather")

    def course_stats(self, tournament_id: str) -> Any:
        """Return per-hole course scoring statistics."""
        return self._root("CourseStats", {"tournamentId": tournament_id}, "courseStats")

    def tournaments(self, ids: list[str]) -> Any:
        """Return metadata for one or more tournament IDs."""
        return self._root("Tournaments", {"ids": ids}, "tournaments")

    def tournament_overview(self, tournament_id: str) -> Any:
        """Return overview tiles, defending champion, and past champions."""
        return self._root(
            "TournamentOverview", {"tournamentId": tournament_id}, "tournamentOverview"
        )

    def tournament_past_results(self, tournament_id: str, year: Optional[int] = None) -> Any:
        """Return a historical leaderboard for an event and optional year."""
        return self._root(
            "TournamentPastResults",
            {"tournamentPastResultsId": tournament_id, "year": year},
            "tournamentPastResults",
        )

    def scorecard_comparison(
        self,
        tournament_id: str,
        player_ids: list[str],
        category: str = "SCORING",
    ) -> Any:
        """Compare a group of players in a scorecard-stat category."""
        return self._root(
            "ScorecardStatsComparisonCategories",
            {
                "tournamentId": tournament_id,
                "playerIds": player_ids,
                "category": category,
            },
            "scorecardStatsComparison",
        )

    # Statistics and content --------------------------------------------

    def stat_overview(self, year: Optional[int] = None, tour: str = "R") -> Any:
        """Return all available stat categories and IDs for a season."""
        return self._root("StatOverview", {"tourCode": tour, "year": year}, "statOverview")

    def stats(
        self,
        stat_id: str,
        year: Optional[int] = None,
        tour: str = "R",
        event_query: Optional[str] = None,
    ) -> Any:
        """Return a raw ranking table for one stat and season."""
        return self._root(
            "StatDetails",
            {"tourCode": tour, "statId": stat_id, "year": year, "eventQuery": event_query},
            "statDetails",
        )

    def fedex_cup(
        self,
        year: Optional[int] = None,
        tour: str = "R",
        event_query: Optional[str] = None,
    ) -> Any:
        """Return raw FedExCup or equivalent tour standings."""
        return self._root(
            "TourCupSplit",
            {"tourCode": tour, "id": None, "year": year, "eventQuery": event_query},
            "tourCupSplit",
        )

    def signature_standings(self, tour: str = "R") -> Any:
        """Return signature-event or Aon standings."""
        return self._root(
            "SignatureStandings", {"tourCode": tour}, "signatureStandings"
        )

    def priority_rankings(self, year: Optional[int] = None, tour: str = "R") -> Any:
        """Return exemption and priority-ranking categories."""
        return self._root(
            "PriorityRankings",
            {"tourCode": tour, "year": year},
            "priorityRankings",
        )

    def course_stats_overview(self, year: Optional[int] = None, tour: str = "R") -> Any:
        """Return the season's course-statistics overview."""
        return self._root(
            "CourseStatsOverview",
            {"tourCode": tour, "year": year},
            "courseStatsOverview",
        )

    def player_tournament_status(self, player_id: str) -> Any:
        """Return a player's status in the active tournament, if present."""
        return self._root(
            "getPlayerTournamentStatus",
            {"playerId": player_id},
            "playerTournamentStatus",
        )

    def news(
        self,
        tour: str = "R",
        limit: int = 20,
        offset: int = 0,
        franchises: Optional[list[str]] = None,
        player_ids: Optional[list[str]] = None,
    ) -> Any:
        """Return paginated news with optional franchise and player filters."""
        return self._root(
            "NewsArticles",
            {
                "tour": tour,
                "franchises": franchises,
                "playerIds": player_ids,
                "limit": limit,
                "offset": offset,
                "tags": None,
                "sectionName": None,
            },
            "newsArticles",
        )

    def news_franchises(self, tour: str = "R", all_franchises: bool = True) -> Any:
        """Return news categories available for a tour."""
        return self._root(
            "NewsFranchises",
            {"tourCode": tour, "allFranchises": all_franchises},
            "newsFranchises",
        )

    def videos(
        self,
        tournament_id: Optional[str] = None,
        player_ids: Optional[list[str]] = None,
        limit: int = 18,
        offset: int = 0,
    ) -> Any:
        """Return video highlights with optional tournament/player filters."""
        return self._root(
            "Videos",
            {
                "tournamentId": tournament_id,
                "playerIds": player_ids,
                "category": None,
                "franchise": None,
                "franchises": None,
                "tourCode": "R",
                "season": None,
                "limit": limit,
                "offset": offset,
                "holeNumber": None,
                "rating": None,
            },
            "videos",
        )

    def tourcast_videos(
        self,
        tournament_id: str,
        player_id: str,
        round: int,
        hole: Optional[int] = None,
        shot: Optional[int] = None,
    ) -> Any:
        """Return TOURCAST clips for a player, round, hole, or shot."""
        return self._root(
            "TourcastVideos",
            {
                "tournamentId": tournament_id,
                "playerId": player_id,
                "round": round,
                "hole": hole,
                "shot": shot,
            },
            "tourcastVideos",
        )

    def content(self, path: str) -> Any:
        """Return and decode a generic CMS fragment by site path."""
        return self._compressed(
            "GenericContentCompressed",
            {"path": path},
            "genericContentCompressed",
        )

    def dp_world_tour_eligibility(self, year: Optional[int] = None,
                                  tour: str = "R") -> Any:
        """Return raw DP World Tour Race to Dubai eligibility standings."""
        return self._root("TourCupSplit", {"tourCode": tour, "id": "2700",
                          "year": year, "eventQuery": None}, "tourCupSplit")

    def playoff_scorecard(self, tournament_id: str) -> Any:
        """Return raw playoff scorecard summaries and hole scores."""
        return self._root("PlayoffScorecardV3", {"tournamentId": tournament_id},
                          "playoffScorecardV3")

    def playoff_shot_details(self, tournament_id: str) -> Any:
        """Return decoded playoff shot details."""
        return self._compressed("PlayoffShotDetailsCompressed", {"tournamentId": tournament_id},
                                "playoffShotDetailsCompressed")

    def team_stroke_play_leaderboard(self, tournament_id: str) -> Any:
        """Return decoded team-stroke-play standings."""
        return self._compressed("TeamStrokePlayLeaderboardCompressed",
            {"teamStrokePlayLeaderboardCompressedId": tournament_id},
            "teamStrokePlayLeaderboardCompressed")

    def match_play_leaderboard(self, tournament_id: str) -> Any:
        """Return decoded match-play rounds, brackets, matches and players."""
        return self._compressed("MatchPlayLeaderboardCompressed",
            {"matchPlayLeaderboardCompressedId": tournament_id},
            "matchPlayLeaderboardCompressed")

    def cup_team_roster(self, tournament_id: str) -> Any:
        """Return team/cup roster and player results."""
        return self._root("CupTeamRoster", {"tournamentId": tournament_id}, "cupTeamRoster")

    def power_rankings(self, path: str) -> Any:
        """Return a raw editorial Power Rankings content fragment."""
        return self._root("GetPowerRankingsTable", {"path": path}, "getPowerRankingsTable")

    def expert_picks(self, path: str) -> Any:
        """Return a raw editorial Expert Picks content fragment."""
        return self._root("GetExpertPicksTable", {"path": path}, "getExpertPicksTable")


    def scorecard_stats(self, tournament_id: str, player_id: str) -> Any:
        """Return decoded player tournament statistics, including every round."""
        return self._compressed("ScorecardStatsV3Compressed",
            {"scorecardStatsV3CompressedId": tournament_id, "playerId": player_id},
            "scorecardStatsV3Compressed")

    def course_stats_details(self, query_type: str = "TOUGHEST_COURSE",
                             year: int | None = None, tour: str = "R",
                             round: str = "ALL") -> Any:
        """Return the full course/hole ranking table and selector metadata."""
        return self._root("CourseStatsDetails",
            {"tourCode": tour, "queryType": query_type, "year": year, "round": round},
            "courseStatsDetails")

    def record_catalog(self, tour: str = "R") -> Any:
        """Return the hierarchical all-time record catalogue."""
        return self._root("AllTimeRecordCategories", {"tourCode": tour},
                          "allTimeRecordCategories")

    def all_time_records(self, record_id: str, tour: str = "R") -> Any:
        """Return one all-time record table, unchanged from the source."""
        return self._root("AllTimeRecordStat",
            {"tourCode": tour, "recordId": record_id}, "allTimeRecordStat")

    def player_comparison(self, player_ids: list[str], category: str = "SCORING",
                          year: int | None = None, tour: str = "R",
                          tournament_id: str | None = None) -> Any:
        """Return the PGA TOUR season/category player comparison table."""
        return self._root("PlayerComparison", {
            "tourCode": tour, "playerIds": player_ids, "category": category,
            "year": year, "tournamentId": tournament_id}, "playerComparison")

    def university_rankings(self, year: int | None = None, week: int | None = None) -> Any:
        """Return PGA TOUR University rankings, selectors and event histories."""
        return self._root("UniversityRankings", {"year": year, "week": week}, "universityRankings")

    def university_total_points(self, season: int | None = None, week: int | None = None) -> Any:
        """Return PGA TOUR University combined points table."""
        return self._root("UniversityTotalPoints", {"season": season, "week": week}, "universityTotalPoints")

api_key = api_key or os.environ.get('PGA_API_KEY') or DEFAULT_BROWSER_KEY instance-attribute

min_interval = min_interval instance-attribute

timeout = timeout instance-attribute

user_agent = user_agent instance-attribute

__init__(api_key: Optional[str] = None, min_interval: float = 1.0, timeout: float = 30.0, user_agent: str = 'pga-tour-unofficial-api/0.1') -> None

Source code in src/pga_tour_api/raw.py
def __init__(
    self,
    api_key: Optional[str] = None,
    min_interval: float = 1.0,
    timeout: float = 30.0,
    user_agent: str = "pga-tour-unofficial-api/0.1",
) -> None:
    self.api_key = api_key or os.environ.get("PGA_API_KEY") or DEFAULT_BROWSER_KEY
    self.min_interval = min_interval
    self.timeout = timeout
    self.user_agent = user_agent
    self._last_request = 0.0

decompress(payload: str) -> Any staticmethod

Decode a base64(gzip(JSON)) response payload.

Source code in src/pga_tour_api/raw.py
@staticmethod
def decompress(payload: str) -> Any:
    """Decode a ``base64(gzip(JSON))`` response payload."""
    try:
        return json.loads(gzip.decompress(base64.b64decode(payload)))
    except Exception as exc:
        raise PgaApiError(f"could not decode compressed payload: {exc}") from exc

graphql(operation: str, variables: Optional[dict[str, Any]] = None) -> dict[str, Any]

Run a bundled GraphQL operation and return its data object.

Source code in src/pga_tour_api/raw.py
def graphql(self, operation: str, variables: Optional[dict[str, Any]] = None) -> dict[str, Any]:
    """Run a bundled GraphQL operation and return its ``data`` object."""
    response = self._request(
        GRAPHQL_URL,
        {
            "operationName": operation,
            "query": self._query_text(operation),
            "variables": variables or {},
        },
    )
    if response.get("errors"):
        messages = "; ".join(
            item.get("message", "") for item in response["errors"]
        )
        raise PgaApiError(messages)
    return response.get("data", {})

rest(path: str) -> Any

GET a path from data-api.pgatour.com.

Source code in src/pga_tour_api/raw.py
def rest(self, path: str) -> Any:
    """GET a path from ``data-api.pgatour.com``."""
    return self._request(f"{REST_URL}/{path.lstrip('/')}")

config() -> dict[str, Any]

Return the frontend's current tournaments and active seasons.

Source code in src/pga_tour_api/raw.py
def config(self) -> dict[str, Any]:
    """Return the frontend's current tournaments and active seasons."""
    return self._request(f"{CONFIG_URL}/web-config")

current_tournament(tour: str = 'R') -> str

Return the default/current tournament ID for a tour.

Source code in src/pga_tour_api/raw.py
def current_tournament(self, tour: str = "R") -> str:
    """Return the default/current tournament ID for a tour."""
    events = self.config().get("defaultTournaments", {}).get(tour, [])
    if not events:
        raise PgaApiError(f"no current tournament for tour {tour!r}")
    return events[0].get("leaderboardId") or events[0]["id"]

schedule(year: int, tour: str = 'R') -> dict[str, Any]

Return a season schedule.

Source code in src/pga_tour_api/raw.py
def schedule(self, year: int, tour: str = "R") -> dict[str, Any]:
    """Return a season schedule."""
    return self.rest(f"schedule/{tour}/{year}")

players(tour: str = 'R') -> dict[str, Any]

Return the full player directory for a tour.

Source code in src/pga_tour_api/raw.py
def players(self, tour: str = "R") -> dict[str, Any]:
    """Return the full player directory for a tour."""
    return self.rest(f"player/list/{tour}")

player_profile(player_id: str) -> dict[str, Any]

Return profile overview data for a player.

Source code in src/pga_tour_api/raw.py
def player_profile(self, player_id: str) -> dict[str, Any]:
    """Return profile overview data for a player."""
    return self.rest(f"player/profiles/{player_id}")

player_career(player_id: str) -> dict[str, Any]

Return career achievements and totals for a player.

Source code in src/pga_tour_api/raw.py
def player_career(self, player_id: str) -> dict[str, Any]:
    """Return career achievements and totals for a player."""
    return self.rest(f"player/profiles/{player_id}/career")

player_results(player_id: str, season: Optional[int] = None) -> dict[str, Any]

Return tournament results for a player, optionally for one season.

Source code in src/pga_tour_api/raw.py
def player_results(self, player_id: str, season: Optional[int] = None) -> dict[str, Any]:
    """Return tournament results for a player, optionally for one season."""
    suffix = f"?season={season}" if season is not None else ""
    return self.rest(f"player/profiles/{player_id}/results{suffix}")

player_stats(player_id: str) -> dict[str, Any]

Return the complete profile statistics response for a player.

Source code in src/pga_tour_api/raw.py
def player_stats(self, player_id: str) -> dict[str, Any]:
    """Return the complete profile statistics response for a player."""
    return self.rest(f"player/profiles/{player_id}/stats")

player_bio(player_id: str) -> dict[str, Any]

Return biography and amateur highlights for a player.

Source code in src/pga_tour_api/raw.py
def player_bio(self, player_id: str) -> dict[str, Any]:
    """Return biography and amateur highlights for a player."""
    return self.rest(f"player/profiles/{player_id}/bio")

odds_markets(tournament_id: str) -> Any

Return the active betting-market catalog for a tournament.

Source code in src/pga_tour_api/raw.py
def odds_markets(self, tournament_id: str) -> Any:
    """Return the active betting-market catalog for a tournament."""
    return self.rest(f"odds/tournament/{tournament_id}")

player_odds(tournament_id: str, player_id: str) -> Any

Return active betting markets for one player.

Source code in src/pga_tour_api/raw.py
def player_odds(self, tournament_id: str, player_id: str) -> Any:
    """Return active betting markets for one player."""
    return self.rest(f"odds/tournament/{tournament_id}/player/{player_id}")

odds_interactivity() -> Any

Return configuration used by the odds widgets.

Source code in src/pga_tour_api/raw.py
def odds_interactivity(self) -> Any:
    """Return configuration used by the odds widgets."""
    return self.rest("odds/interactivity")

speed_rounds(tour: str = 'R') -> Any

Return the speed-round video index for a tour.

Source code in src/pga_tour_api/raw.py
def speed_rounds(self, tour: str = "R") -> Any:
    """Return the speed-round video index for a tour."""
    return self.rest(f"content/watch/speedRounds/{tour}")

leaderboard(tournament_id: str) -> Any

Return a decoded full leaderboard.

Source code in src/pga_tour_api/raw.py
def leaderboard(self, tournament_id: str) -> Any:
    """Return a decoded full leaderboard."""
    return self._compressed(
        "LeaderboardCompressedV3",
        {"leaderboardCompressedV3Id": tournament_id},
        "leaderboardCompressedV3",
    )

current_leaders(tournament_id: str) -> Any

Return and decode the compact current-leaders payload.

Source code in src/pga_tour_api/raw.py
def current_leaders(self, tournament_id: str) -> Any:
    """Return and decode the compact current-leaders payload."""
    return self._compressed(
        "CurrentLeadersCompressed",
        {"tournamentId": tournament_id},
        "currentLeadersCompressed",
    )

field(tournament_id: str, include_withdrawn: bool = True) -> Any

Return the event field, alternates, and optional withdrawals.

Source code in src/pga_tour_api/raw.py
def field(self, tournament_id: str, include_withdrawn: bool = True) -> Any:
    """Return the event field, alternates, and optional withdrawals."""
    return self._root(
        "Field",
        {"fieldId": tournament_id, "includeWithdrawn": include_withdrawn, "changesOnly": False},
        "field",
    )

field_stats(tournament_id: str, stat_type: str = 'CURRENT_FORM') -> Any

Return current-form or course-fit data for the event field.

Source code in src/pga_tour_api/raw.py
def field_stats(self, tournament_id: str, stat_type: str = "CURRENT_FORM") -> Any:
    """Return current-form or course-fit data for the event field."""
    return self._root(
        "FieldStats",
        {"tournamentId": tournament_id, "fieldStatType": stat_type},
        "fieldStats",
    )

leaderboard_holes(tournament_id: str, round: Optional[int] = None) -> Any

Return whole-field hole-by-hole scores for a round.

Source code in src/pga_tour_api/raw.py
def leaderboard_holes(self, tournament_id: str, round: Optional[int] = None) -> Any:
    """Return whole-field hole-by-hole scores for a round."""
    return self._root(
        "LeaderboardHoleByHole",
        {"tournamentId": tournament_id, "round": round},
        "leaderboardHoleByHole",
    )

tee_times(tournament_id: str) -> Any

Return and decode tee groups and player assignments.

Source code in src/pga_tour_api/raw.py
def tee_times(self, tournament_id: str) -> Any:
    """Return and decode tee groups and player assignments."""
    return self._compressed(
        "TeeTimesCompressedV2",
        {"teeTimesCompressedV2Id": tournament_id},
        "teeTimesCompressedV2",
    )

scorecard(tournament_id: str, player_id: str) -> Any

Return and decode one player's hole-by-hole scorecard.

Source code in src/pga_tour_api/raw.py
def scorecard(self, tournament_id: str, player_id: str) -> Any:
    """Return and decode one player's hole-by-hole scorecard."""
    return self._compressed(
        "ScorecardCompressedV3",
        {"tournamentId": tournament_id, "playerId": player_id},
        "scorecardCompressedV3",
    )

shot_details(tournament_id: str, player_id: str, round: int, include_radar: bool = False) -> Any

Return and decode shot-level tracking for one player and round.

Source code in src/pga_tour_api/raw.py
def shot_details(
    self,
    tournament_id: str,
    player_id: str,
    round: int,
    include_radar: bool = False,
) -> Any:
    """Return and decode shot-level tracking for one player and round."""
    return self._compressed(
        "shotDetailsV4Compressed",
        {
            "tournamentId": tournament_id,
            "playerId": player_id,
            "round": round,
            "includeRadar": include_radar,
        },
        "shotDetailsV4Compressed",
    )

odds(tournament_id: str) -> Any

Return and decode tournament winner odds.

Source code in src/pga_tour_api/raw.py
def odds(self, tournament_id: str) -> Any:
    """Return and decode tournament winner odds."""
    return self._compressed(
        "oddsToWinCompressed",
        {"tournamentId": tournament_id},
        "oddsToWinCompressed",
    )

coverage(tournament_id: str) -> Any

Return television and streaming coverage windows.

Source code in src/pga_tour_api/raw.py
def coverage(self, tournament_id: str) -> Any:
    """Return television and streaming coverage windows."""
    return self._root("Coverage", {"tournamentId": tournament_id}, "coverage")

weather(tournament_id: str) -> Any

Return hourly and daily tournament weather forecasts.

Source code in src/pga_tour_api/raw.py
def weather(self, tournament_id: str) -> Any:
    """Return hourly and daily tournament weather forecasts."""
    return self._root("Weather", {"tournamentId": tournament_id}, "weather")

course_stats(tournament_id: str) -> Any

Return per-hole course scoring statistics.

Source code in src/pga_tour_api/raw.py
def course_stats(self, tournament_id: str) -> Any:
    """Return per-hole course scoring statistics."""
    return self._root("CourseStats", {"tournamentId": tournament_id}, "courseStats")

tournaments(ids: list[str]) -> Any

Return metadata for one or more tournament IDs.

Source code in src/pga_tour_api/raw.py
def tournaments(self, ids: list[str]) -> Any:
    """Return metadata for one or more tournament IDs."""
    return self._root("Tournaments", {"ids": ids}, "tournaments")

tournament_overview(tournament_id: str) -> Any

Return overview tiles, defending champion, and past champions.

Source code in src/pga_tour_api/raw.py
def tournament_overview(self, tournament_id: str) -> Any:
    """Return overview tiles, defending champion, and past champions."""
    return self._root(
        "TournamentOverview", {"tournamentId": tournament_id}, "tournamentOverview"
    )

tournament_past_results(tournament_id: str, year: Optional[int] = None) -> Any

Return a historical leaderboard for an event and optional year.

Source code in src/pga_tour_api/raw.py
def tournament_past_results(self, tournament_id: str, year: Optional[int] = None) -> Any:
    """Return a historical leaderboard for an event and optional year."""
    return self._root(
        "TournamentPastResults",
        {"tournamentPastResultsId": tournament_id, "year": year},
        "tournamentPastResults",
    )

scorecard_comparison(tournament_id: str, player_ids: list[str], category: str = 'SCORING') -> Any

Compare a group of players in a scorecard-stat category.

Source code in src/pga_tour_api/raw.py
def scorecard_comparison(
    self,
    tournament_id: str,
    player_ids: list[str],
    category: str = "SCORING",
) -> Any:
    """Compare a group of players in a scorecard-stat category."""
    return self._root(
        "ScorecardStatsComparisonCategories",
        {
            "tournamentId": tournament_id,
            "playerIds": player_ids,
            "category": category,
        },
        "scorecardStatsComparison",
    )

stat_overview(year: Optional[int] = None, tour: str = 'R') -> Any

Return all available stat categories and IDs for a season.

Source code in src/pga_tour_api/raw.py
def stat_overview(self, year: Optional[int] = None, tour: str = "R") -> Any:
    """Return all available stat categories and IDs for a season."""
    return self._root("StatOverview", {"tourCode": tour, "year": year}, "statOverview")

stats(stat_id: str, year: Optional[int] = None, tour: str = 'R', event_query: Optional[str] = None) -> Any

Return a raw ranking table for one stat and season.

Source code in src/pga_tour_api/raw.py
def stats(
    self,
    stat_id: str,
    year: Optional[int] = None,
    tour: str = "R",
    event_query: Optional[str] = None,
) -> Any:
    """Return a raw ranking table for one stat and season."""
    return self._root(
        "StatDetails",
        {"tourCode": tour, "statId": stat_id, "year": year, "eventQuery": event_query},
        "statDetails",
    )

fedex_cup(year: Optional[int] = None, tour: str = 'R', event_query: Optional[str] = None) -> Any

Return raw FedExCup or equivalent tour standings.

Source code in src/pga_tour_api/raw.py
def fedex_cup(
    self,
    year: Optional[int] = None,
    tour: str = "R",
    event_query: Optional[str] = None,
) -> Any:
    """Return raw FedExCup or equivalent tour standings."""
    return self._root(
        "TourCupSplit",
        {"tourCode": tour, "id": None, "year": year, "eventQuery": event_query},
        "tourCupSplit",
    )

signature_standings(tour: str = 'R') -> Any

Return signature-event or Aon standings.

Source code in src/pga_tour_api/raw.py
def signature_standings(self, tour: str = "R") -> Any:
    """Return signature-event or Aon standings."""
    return self._root(
        "SignatureStandings", {"tourCode": tour}, "signatureStandings"
    )

priority_rankings(year: Optional[int] = None, tour: str = 'R') -> Any

Return exemption and priority-ranking categories.

Source code in src/pga_tour_api/raw.py
def priority_rankings(self, year: Optional[int] = None, tour: str = "R") -> Any:
    """Return exemption and priority-ranking categories."""
    return self._root(
        "PriorityRankings",
        {"tourCode": tour, "year": year},
        "priorityRankings",
    )

course_stats_overview(year: Optional[int] = None, tour: str = 'R') -> Any

Return the season's course-statistics overview.

Source code in src/pga_tour_api/raw.py
def course_stats_overview(self, year: Optional[int] = None, tour: str = "R") -> Any:
    """Return the season's course-statistics overview."""
    return self._root(
        "CourseStatsOverview",
        {"tourCode": tour, "year": year},
        "courseStatsOverview",
    )

player_tournament_status(player_id: str) -> Any

Return a player's status in the active tournament, if present.

Source code in src/pga_tour_api/raw.py
def player_tournament_status(self, player_id: str) -> Any:
    """Return a player's status in the active tournament, if present."""
    return self._root(
        "getPlayerTournamentStatus",
        {"playerId": player_id},
        "playerTournamentStatus",
    )

news(tour: str = 'R', limit: int = 20, offset: int = 0, franchises: Optional[list[str]] = None, player_ids: Optional[list[str]] = None) -> Any

Return paginated news with optional franchise and player filters.

Source code in src/pga_tour_api/raw.py
def news(
    self,
    tour: str = "R",
    limit: int = 20,
    offset: int = 0,
    franchises: Optional[list[str]] = None,
    player_ids: Optional[list[str]] = None,
) -> Any:
    """Return paginated news with optional franchise and player filters."""
    return self._root(
        "NewsArticles",
        {
            "tour": tour,
            "franchises": franchises,
            "playerIds": player_ids,
            "limit": limit,
            "offset": offset,
            "tags": None,
            "sectionName": None,
        },
        "newsArticles",
    )

news_franchises(tour: str = 'R', all_franchises: bool = True) -> Any

Return news categories available for a tour.

Source code in src/pga_tour_api/raw.py
def news_franchises(self, tour: str = "R", all_franchises: bool = True) -> Any:
    """Return news categories available for a tour."""
    return self._root(
        "NewsFranchises",
        {"tourCode": tour, "allFranchises": all_franchises},
        "newsFranchises",
    )

videos(tournament_id: Optional[str] = None, player_ids: Optional[list[str]] = None, limit: int = 18, offset: int = 0) -> Any

Return video highlights with optional tournament/player filters.

Source code in src/pga_tour_api/raw.py
def videos(
    self,
    tournament_id: Optional[str] = None,
    player_ids: Optional[list[str]] = None,
    limit: int = 18,
    offset: int = 0,
) -> Any:
    """Return video highlights with optional tournament/player filters."""
    return self._root(
        "Videos",
        {
            "tournamentId": tournament_id,
            "playerIds": player_ids,
            "category": None,
            "franchise": None,
            "franchises": None,
            "tourCode": "R",
            "season": None,
            "limit": limit,
            "offset": offset,
            "holeNumber": None,
            "rating": None,
        },
        "videos",
    )

tourcast_videos(tournament_id: str, player_id: str, round: int, hole: Optional[int] = None, shot: Optional[int] = None) -> Any

Return TOURCAST clips for a player, round, hole, or shot.

Source code in src/pga_tour_api/raw.py
def tourcast_videos(
    self,
    tournament_id: str,
    player_id: str,
    round: int,
    hole: Optional[int] = None,
    shot: Optional[int] = None,
) -> Any:
    """Return TOURCAST clips for a player, round, hole, or shot."""
    return self._root(
        "TourcastVideos",
        {
            "tournamentId": tournament_id,
            "playerId": player_id,
            "round": round,
            "hole": hole,
            "shot": shot,
        },
        "tourcastVideos",
    )

content(path: str) -> Any

Return and decode a generic CMS fragment by site path.

Source code in src/pga_tour_api/raw.py
def content(self, path: str) -> Any:
    """Return and decode a generic CMS fragment by site path."""
    return self._compressed(
        "GenericContentCompressed",
        {"path": path},
        "genericContentCompressed",
    )

dp_world_tour_eligibility(year: Optional[int] = None, tour: str = 'R') -> Any

Return raw DP World Tour Race to Dubai eligibility standings.

Source code in src/pga_tour_api/raw.py
def dp_world_tour_eligibility(self, year: Optional[int] = None,
                              tour: str = "R") -> Any:
    """Return raw DP World Tour Race to Dubai eligibility standings."""
    return self._root("TourCupSplit", {"tourCode": tour, "id": "2700",
                      "year": year, "eventQuery": None}, "tourCupSplit")

playoff_scorecard(tournament_id: str) -> Any

Return raw playoff scorecard summaries and hole scores.

Source code in src/pga_tour_api/raw.py
def playoff_scorecard(self, tournament_id: str) -> Any:
    """Return raw playoff scorecard summaries and hole scores."""
    return self._root("PlayoffScorecardV3", {"tournamentId": tournament_id},
                      "playoffScorecardV3")

playoff_shot_details(tournament_id: str) -> Any

Return decoded playoff shot details.

Source code in src/pga_tour_api/raw.py
def playoff_shot_details(self, tournament_id: str) -> Any:
    """Return decoded playoff shot details."""
    return self._compressed("PlayoffShotDetailsCompressed", {"tournamentId": tournament_id},
                            "playoffShotDetailsCompressed")

team_stroke_play_leaderboard(tournament_id: str) -> Any

Return decoded team-stroke-play standings.

Source code in src/pga_tour_api/raw.py
def team_stroke_play_leaderboard(self, tournament_id: str) -> Any:
    """Return decoded team-stroke-play standings."""
    return self._compressed("TeamStrokePlayLeaderboardCompressed",
        {"teamStrokePlayLeaderboardCompressedId": tournament_id},
        "teamStrokePlayLeaderboardCompressed")

match_play_leaderboard(tournament_id: str) -> Any

Return decoded match-play rounds, brackets, matches and players.

Source code in src/pga_tour_api/raw.py
def match_play_leaderboard(self, tournament_id: str) -> Any:
    """Return decoded match-play rounds, brackets, matches and players."""
    return self._compressed("MatchPlayLeaderboardCompressed",
        {"matchPlayLeaderboardCompressedId": tournament_id},
        "matchPlayLeaderboardCompressed")

cup_team_roster(tournament_id: str) -> Any

Return team/cup roster and player results.

Source code in src/pga_tour_api/raw.py
def cup_team_roster(self, tournament_id: str) -> Any:
    """Return team/cup roster and player results."""
    return self._root("CupTeamRoster", {"tournamentId": tournament_id}, "cupTeamRoster")

power_rankings(path: str) -> Any

Return a raw editorial Power Rankings content fragment.

Source code in src/pga_tour_api/raw.py
def power_rankings(self, path: str) -> Any:
    """Return a raw editorial Power Rankings content fragment."""
    return self._root("GetPowerRankingsTable", {"path": path}, "getPowerRankingsTable")

expert_picks(path: str) -> Any

Return a raw editorial Expert Picks content fragment.

Source code in src/pga_tour_api/raw.py
def expert_picks(self, path: str) -> Any:
    """Return a raw editorial Expert Picks content fragment."""
    return self._root("GetExpertPicksTable", {"path": path}, "getExpertPicksTable")

scorecard_stats(tournament_id: str, player_id: str) -> Any

Return decoded player tournament statistics, including every round.

Source code in src/pga_tour_api/raw.py
def scorecard_stats(self, tournament_id: str, player_id: str) -> Any:
    """Return decoded player tournament statistics, including every round."""
    return self._compressed("ScorecardStatsV3Compressed",
        {"scorecardStatsV3CompressedId": tournament_id, "playerId": player_id},
        "scorecardStatsV3Compressed")

course_stats_details(query_type: str = 'TOUGHEST_COURSE', year: int | None = None, tour: str = 'R', round: str = 'ALL') -> Any

Return the full course/hole ranking table and selector metadata.

Source code in src/pga_tour_api/raw.py
def course_stats_details(self, query_type: str = "TOUGHEST_COURSE",
                         year: int | None = None, tour: str = "R",
                         round: str = "ALL") -> Any:
    """Return the full course/hole ranking table and selector metadata."""
    return self._root("CourseStatsDetails",
        {"tourCode": tour, "queryType": query_type, "year": year, "round": round},
        "courseStatsDetails")

record_catalog(tour: str = 'R') -> Any

Return the hierarchical all-time record catalogue.

Source code in src/pga_tour_api/raw.py
def record_catalog(self, tour: str = "R") -> Any:
    """Return the hierarchical all-time record catalogue."""
    return self._root("AllTimeRecordCategories", {"tourCode": tour},
                      "allTimeRecordCategories")

all_time_records(record_id: str, tour: str = 'R') -> Any

Return one all-time record table, unchanged from the source.

Source code in src/pga_tour_api/raw.py
def all_time_records(self, record_id: str, tour: str = "R") -> Any:
    """Return one all-time record table, unchanged from the source."""
    return self._root("AllTimeRecordStat",
        {"tourCode": tour, "recordId": record_id}, "allTimeRecordStat")

player_comparison(player_ids: list[str], category: str = 'SCORING', year: int | None = None, tour: str = 'R', tournament_id: str | None = None) -> Any

Return the PGA TOUR season/category player comparison table.

Source code in src/pga_tour_api/raw.py
def player_comparison(self, player_ids: list[str], category: str = "SCORING",
                      year: int | None = None, tour: str = "R",
                      tournament_id: str | None = None) -> Any:
    """Return the PGA TOUR season/category player comparison table."""
    return self._root("PlayerComparison", {
        "tourCode": tour, "playerIds": player_ids, "category": category,
        "year": year, "tournamentId": tournament_id}, "playerComparison")

university_rankings(year: int | None = None, week: int | None = None) -> Any

Return PGA TOUR University rankings, selectors and event histories.

Source code in src/pga_tour_api/raw.py
def university_rankings(self, year: int | None = None, week: int | None = None) -> Any:
    """Return PGA TOUR University rankings, selectors and event histories."""
    return self._root("UniversityRankings", {"year": year, "week": week}, "universityRankings")

university_total_points(season: int | None = None, week: int | None = None) -> Any

Return PGA TOUR University combined points table.

Source code in src/pga_tour_api/raw.py
def university_total_points(self, season: int | None = None, week: int | None = None) -> Any:
    """Return PGA TOUR University combined points table."""
    return self._root("UniversityTotalPoints", {"season": season, "week": week}, "universityTotalPoints")

pga_tour_api.PgaApiError

Bases: RuntimeError

Raised for transport, HTTP, GraphQL, and payload-decoding failures.

Source code in src/pga_tour_api/raw.py
class PgaApiError(RuntimeError):
    """Raised for transport, HTTP, GraphQL, and payload-decoding failures."""

Detailed statistics and records

The raw client also exposes scorecard_stats(tournament_id, player_id), course_stats_details(query_type="TOUGHEST_COURSE", year=None, tour="R", round="ALL"), record_catalog(tour="R"), and all_time_records(record_id, tour="R"). These preserve upstream structures; scorecard statistics are automatically decompressed.

The raw client also exposes player_comparison(player_ids, category="SCORING", year=None, tour="R", tournament_id=None), returning the PGA comparison table unchanged.

It also exposes university_rankings(year=None, week=None) and university_total_points(season=None, week=None).

dp_world_tour_eligibility(year=None, tour="R") returns the raw Race to Dubai eligibility standings.

power_rankings(path) and expert_picks(path) return the raw editorial tables for a content-fragment path embedded in a PGA TOUR article.