Skip to contents

Play-by-play of one finished game (2022-23 on) in game order, with shot x / y and the clip references.

Endpoint: GET https://pleagueofficial.com/api/playbyplay.php?uuid={uuid} (after the game page, which holds the uuid)

Usage

pleague_pbp(gid)

Arguments

gid

Required. P. LEAGUE+ game id on the site: ONE global integer sequence over every stage and season (197 = the 2022-23 opener, 662 = the 2025-26 opener), e.g. 197; gid in pleague_schedule. Only finished games are served.

Value

A hoopR_data tibble: Play-by-play of one finished game (2022-23 on) in game order, with shot x / y and the clip references. An empty valid answer is a zero-row tibble with these columns, in this order (sportsdataverse-py pleague_pbp):

col_nametypedescription
gidintegerP. LEAGUE+ game id on the site: ONE global integer sequence over every stage and season (1-12 = 2020-21 pre-season, 13 = the 2020-21 opener, 197 = the 2022-23 opener, 662 = the 2025-26 opener), with gaps; a row id, not a count. The {gid} of /game/{gid}.
seqintegerPosition of the play in game order, 1-based (the fragment lists plays oldest first within the fragment).
periodcharacterPeriod label: Q1-Q4, then OT1, OT2 for overtime.
period_nointeger1-based position of the period: 1-4 for Q1-Q4, then 5 for OT1, 6 for OT2.
clockcharacterGame clock REMAINING in the period 'mm:ss' (counts down; Q1 opens at 12:00, OT at 05:00).
seconds_leftintegerThe clock as whole seconds remaining in the period (08:34 = 514), derived by the parser.
sidecharacterSide of the acting team: away or home (the fragment places the play in the away or home column).
jerseycharacterShirt number of the acting player as printed; null on team rows and where the name is printed without a number.
player_zhcharacterActing player's name as printed; null on team rows (a team rebound, timeout or shot-clock violation). The page prints no player ids: join to the box on side, jersey and name_zh.
actioncharacterAction code (one of the codes listed under "Action codes" below; an unseen phrase is refused as layout drift).
action_zhcharacterThe action phrase as printed, Traditional Chinese ((Chinese text) = dunk, made; '#35 (Chinese text)' = '#35 {player} dunk, made'); blank on an unlabelled row. Each of the 82 observed phrases maps to one action code (see action).
action_encharacterEnglish meaning of the play's phrase (see action), so a free throw reads its own trip, e.g. 'free throw 2, made'; null for a code outside the observed table.
text_zhcharacterThe whole printed text of the play, e.g. '#35 (Chinese text)' = '#35 {player} dunk, made'.
ft_numberintegerWhich free throw of the trip this is (1, 2 or 3), from (Chinese text); null on every other play.
ft_awardedintegerFree throws awarded in the trip (1, 2 or 3) from (Chinese text); set only on the FIRST free throw's row, null otherwise.
pointsintegerPoints scored by the play from the '+2' / '+3' / '+1' tag; null when the tag is a bare '+'.
score_awayintegerAway score after the play, from the 'A - H' cell (AWAY first, unlike the box); null on plays that print no score (substitutions, rebounds, jump balls).
score_homeintegerHome score after the play, from the 'A - H' cell; null when the play prints no score.
xdoubleShot / play x: PERCENT (0-100) of the court LENGTH in the PHYSICAL court frame (not normalised per team): baskets near x = 5 and x = 95, teams swap ends at half-time. The Preciser shot list uses a home-attacks-95 frame: x_vendor = 100 - x in the halves where the home team attacks x < 50. Null on plays without a location. Court 28.75 m long.
ydoubleShot / play y: PERCENT (0-100) of the court WIDTH in the physical frame (baskets at y = 50); y_vendor = 100 - y in the halves where the home team attacks x < 50. Which sideline is y = 0 is unverified. Court 15.25 m wide.
nice_playlogicalTrue when the site stars the play (a nice_play row class and a starred clip).
video_uuidcharacterClip uuid (key space: the clip vendor tryspot); null on plays without a clip (about 3 in 4). The clip urls are never kept: they carry a vendor access key.
synergy_play_idcharacterSynergy Sports play uuid of the clip (the site links Synergy); a string on every one of the 50,862 clips in the corpus.
video_stat_categorycharacterThe clip's own stat label (ast, blk, ft_made, ft_miss, pfoul, reb_d, reb_o, stl, three_made, three_miss, timeout, turnover, two_made, two_miss): it names the PLAY the clip shows, not always this row's action (a rebound row carries the missed shot's clip).
tryspot_player_idintegerPlayer id in the clip vendor's key space (an integer such as 59476); NOT the box player_id.
video_created_atcharacterWhen the clip was created, UTC ISO 8601 with milliseconds.

Details

Source, terms of use, request pacing, seasons, ids, coverage and error classes of this family: see the notes in pleague_schedule().

Action codes

Each code of action, its meaning and [the number of plays] in 258,183 plays of 404 finished 2022-23..2025-26 games (82 printed phrases plus the blank row):

  • ast: assist [17,020]

  • blk: block [3,181]

  • fg2_alley_oop_made: 2-point alley-oop, made [205]

  • fg2_alley_oop_missed: 2-point alley-oop, missed [81]

  • fg2_driving_layup_made: 2-point driving layup, made [4,281]

  • fg2_driving_layup_missed: 2-point driving layup, missed [3,961]

  • fg2_dunk_made: 2-point dunk, made [1,316]

  • fg2_dunk_missed: 2-point dunk, missed [162]

  • fg2_fadeaway_jumper_made: 2-point fadeaway jumper, made [583]

  • fg2_fadeaway_jumper_missed: 2-point fadeaway jumper, missed [1,121]

  • fg2_floater_made: 2-point floater, made [1,920]

  • fg2_floater_missed: 2-point floater, missed [3,052]

  • fg2_hook_shot_made: 2-point hook shot, made [610]

  • fg2_hook_shot_missed: 2-point hook shot, missed [773]

  • fg2_jump_shot_made: 2-point jump shot, made [1,657]

  • fg2_jump_shot_missed: 2-point jump shot, missed [3,606]

  • fg2_layup_made: 2-point layup, made [4,428]

  • fg2_layup_missed: 2-point layup, missed [3,403]

  • fg2_pull_up_jumper_made: 2-point pull-up jumper, made [1,004]

  • fg2_pull_up_jumper_missed: 2-point pull-up jumper, missed [1,564]

  • fg2_putback_made: 2-point putback / tip-in, made [2,138]

  • fg2_putback_missed: 2-point putback / tip-in, missed [1,539]

  • fg2_step_back_shot_made: 2-point step-back shot, made [243]

  • fg2_step_back_shot_missed: 2-point step-back shot, missed [410]

  • fg2_turnaround_jumper_made: 2-point turnaround jumper, made [672]

  • fg2_turnaround_jumper_missed: 2-point turnaround jumper, missed [1,158]

  • fg3_fadeaway_jumper_made: 3-point fadeaway jumper, made [48]

  • fg3_fadeaway_jumper_missed: 3-point fadeaway jumper, missed [92]

  • fg3_made: 3-point shot, style not recorded, made [7,817]

  • fg3_missed: 3-point shot, style not recorded, missed [17,046]

  • fg3_pull_up_jumper_made: 3-point pull-up jumper, made [480]

  • fg3_pull_up_jumper_missed: 3-point pull-up jumper, missed [922]

  • fg3_step_back_shot_made: 3-point step-back shot, made [245]

  • fg3_step_back_shot_missed: 3-point step-back shot, missed [559]

  • fg3_turnaround_jumper_made: 3-point turnaround jumper, made [10]

  • fg3_turnaround_jumper_missed: 3-point turnaround jumper, missed [23]

  • fg_made: field goal made, shot type and distance not recorded [158]

  • fg_missed: field goal missed, shot type and distance not recorded [168]

  • foul: foul, kind not recorded (team row) [18]

  • foul_bench_technical: technical foul on the bench [35]

  • foul_coach_technical: technical foul on the coach [75]

  • foul_disqualifying: disqualifying foul [2]

  • foul_drawn: foul drawn (the opponent's foul on this player) [17,035]

  • foul_offensive: offensive foul committed [1,024]

  • foul_personal: personal foul committed [15,745]

  • foul_technical: technical foul [210]

  • foul_unsportsmanlike: unsportsmanlike foul [261]

  • ft_made: free throw 1 of 1 awarded, made [1,326]

  • ft_made: free throw 1 of 3 awarded, made [128]

  • ft_made: free throw 1 of 2 awarded, made [5,187]

  • ft_made: free throw 3, made [149]

  • ft_made: free throw 2, made [5,798]

  • ft_missed: free throw 1 of 1 awarded, missed [669]

  • ft_missed: free throw 1 of 3 awarded, missed [67]

  • ft_missed: free throw 1 of 2 awarded, missed [2,736]

  • ft_missed: free throw 3, missed [46]

  • ft_missed: free throw 2, missed [2,319]

  • jump_ball_lost: jump ball lost [404]

  • jump_ball_won: jump ball won [708]

  • reb_def: defensive rebound (a team rebound has no player) [29,537]

  • reb_off: offensive rebound (a team rebound has no player) [12,379]

  • review_coach_challenge: video review: coach's challenge (2025-26 onward; the printed text ends with a bare comma) [videoReview, coachChallenge,, 82]

  • review_instant_replay: video review: instant-replay system, started by the officials (2025-26 onward) [videoReview, instantReplaySystem,, 67]

  • review_instant_replay_1: video review: instant-replay system, variant printed with a trailing '1' (meaning unexplained; the row also prints the running score and the points tag '+videoReview') [videoReview, instantReplaySystem, 1, 6]

  • stl: steal [7,361]

  • sub_in: substitution: enters the game [25,505]

  • sub_out: substitution: leaves the game [25,532]

  • timeout: timeout (a team row) [2,854]

  • tov_3sec: turnover: 3-second violation [249]

  • tov_5sec: turnover: 5-second violation [40]

  • tov_8sec: turnover: 8-second violation [26]

  • tov_backcourt: turnover: backcourt violation [96]

  • tov_bad_pass: turnover: bad pass [6,325]

  • tov_ball_handling: turnover: ball-handling [3,547]

  • tov_carrying: turnover: carrying the ball [47]

  • tov_double_dribble: turnover: double dribble [53]

  • tov_offensive: turnover: offensive (kind not recorded) [1,007]

  • tov_offensive_goaltending: turnover: offensive goaltending [4]

  • tov_other: turnover: other [51]

  • tov_out_of_bounds: turnover: ball out of bounds [441]

  • tov_shot_clock: turnover: shot-clock violation [489]

  • tov_travel: turnover: travelling [718]

  • unlabelled: row has a player but no action text; in the corpus a shot with x/y followed by a rebound, i.e. a missed shot (inferred) [(blank), 169]

Examples

# \donttest{
  try(pleague_pbp(gid = 197))
#> ── P. LEAGUE+ Play-by-Play from pleagueofficial.com ───────────── hoopR 3.2.0 ──
#> ℹ Data updated: 2026-10-31 21:33:23 UTC
#> # A tibble: 630 × 26
#>      gid   seq period period_no clock seconds_left side  jersey player_zh action
#>    <int> <int> <chr>      <int> <chr>        <int> <chr> <chr>  <chr>     <chr> 
#>  1   197     1 Q1             1 12:00          720 away  35     辛巴      jump_…
#>  2   197     2 Q1             1 12:00          720 home  5      強森      jump_…
#>  3   197     3 Q1             1 11:41          701 away  35     辛巴      fg2_d…
#>  4   197     4 Q1             1 11:41          701 away  31     阿提諾    ast   
#>  5   197     5 Q1             1 11:20          680 home  5      強森      fg3_m…
#>  6   197     6 Q1             1 10:53          653 away  8      朱雲豪    tov_b…
#>  7   197     7 Q1             1 10:53          653 home  5      強森      stl   
#>  8   197     8 Q1             1 10:49          649 home  NA     賴廷恩    fg3_m…
#>  9   197     9 Q1             1 10:42          642 away  35     辛巴      fg2_l…
#> 10   197    10 Q1             1 10:42          642 away  9      田浩      ast   
#> # ℹ 620 more rows
#> # ℹ 16 more variables: action_zh <chr>, action_en <chr>, text_zh <chr>,
#> #   ft_number <int>, ft_awarded <int>, points <int>, score_away <int>,
#> #   score_home <int>, x <dbl>, y <dbl>, nice_play <lgl>, video_uuid <chr>,
#> #   synergy_play_id <chr>, video_stat_category <chr>, tryspot_player_id <int>,
#> #   video_created_at <chr>
# }