Skip to contents

Every field-goal and free-throw attempt of a game (2016-17 onward) with its court position.

One row per attempt. Two coordinate systems share pos_x / pos_y, told apart by coord_system: mm (match ids from 2020-21) is millimetres from the centre of the attacked basket, every shot mirrored to one basket, pos_x towards midcourt and pos_y across the court (negative = the shooter's left); x_basket_m / y_basket_m are those in metres. grid (up to 2019-20) is a 0-100 percent grid over the full court, NOT mirrored (teams switch ends at half time), and has no basket-relative metres (null). Free throws are (0, 0) with a null zone; legacy zones are unreliable. The inference of units and origin is the refs' (arc and sideline fit), not documented by ACB. player_name comes from the game's shooter list.

Endpoint: GET https://api2.acb.com/api/matchdata/MatchShots/match-shots

Usage

acb_shots(match_id)

Arguments

match_id

(required) ACB match id (integer), one global sequence for every competition: legacy ids (seasons up to 2019-20) sit below about 20000 (12559 = the 2005-06 final); ids from 2020-21 start around 100000 (105401 = Barca 89-86 Valencia Basket, Liga 2026-27 round 3). A whole number; a whole double is accepted.

Value

A hoopR_data tibble, one row per record described above; the columns are always these, in this order (sportsdataverse-py acb_shots):

col_nametypedescription
match_idintegerACB match id (one global sequence for every competition; legacy ids are below about 20000 up to 2019-20, ids from 2020-21 start around 100000). Taken from the request when the body does not name the game.
edition_idintegerSeason id (editionId) = season START year - 1935 (91 = 2026-27). The wrappers take the END year, season.
seasonintegerSeason END year (2027 = 2026-27) = edition_id + 1936: the key the wrappers take.
shot_idintegerEvent id of the attempt within one match; joins nothing outside this body.
pos_xintegerCourt x as sent. mm system: millimetres from the attacked basket towards midcourt (never negative; 0 for free throws and dunks); grid system: 0-100 percent of the court length, not mirrored.
pos_yintegerCourt y as sent. mm system: millimetres across the court (sidelines at -7500 and +7500, negative = the shooter's left); grid system: 0-100 percent of the width (which sideline is 0 is unverified). Free throws are (0, 0).
coord_systemcharacterShot coordinate system, told by the match id: mm = millimetres from the attacked basket (match ids from 2020-21), grid = 0-100 percent of the full court (up to 2019-20). Null when the match id is unknown.
x_basket_mdoubleShot x in metres from the centre of the attacked basket towards midcourt, when coord_system is mm and the attempt is a field goal; null otherwise. Units and origin are inferred by the refs (arc and sideline fit), not documented by ACB.
y_basket_mdoubleShot y in metres across the court from the basket line, negative = the shooter's left, when coord_system is mm and the attempt is a field goal; null otherwise (inferred by the refs, not documented by ACB).
quarterintegerPeriod of the attempt: 1-4 quarters, 5+ overtimes.
minuteintegerGame clock left in the period, minutes
secondintegerGame clock left in the period, seconds part
clock_secondsintegerGame clock REMAINING in the period as whole seconds (minute x 60 + second; the clock counts down from 600, overtimes from 300).
play_typeintegerEvent code (92 free throw made, 93 two made, 94 three made, 96 free throw missed, 97 two missed, 98 three missed, 100 dunk, 533 dunk missed).
play_namecharacterBundle name of play_type (acb_codes.PLAY_TYPES, e.g. TWO_POINTER_MADE); null for a code outside the enum such as the minute marker 600.
is_homelogicalTrue when the shooter's team is the home team (the API's local).
score_homeintegerHome score after the attempt.
score_awayintegerAway score after the attempt.
player_idintegerACB licence number of the shooter.
player_namecharacterShooter's short name from the game's shooter list (e.g. 'T. Martin').
shirt_numbercharacterShooter's shirt number as a string, from the game's shooter list.
zonecharacterShot zone (acb_codes.SHOT_ZONES: restricted_area, paint, mid_range_, corner_three_, wing_three_*, top_three); null on free throws; legacy zones (up to 2019-20) are unreliable.
madelogicalTrue when the shot went in; null on some free throws.
pointsintegerPoints the attempt scored (2 or 3 on made field goals); null on free throws and misses.

Details

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

Examples

# \donttest{
  try(acb_shots(match_id = 105401))
#> ── ACB Shots from api2.acb.com ────────────────────────────────── hoopR 3.2.0 ──
#> ℹ Data updated: 2026-10-31 21:26:41 UTC
#> # A tibble: 188 × 24
#>    match_id edition_id season shot_id pos_x pos_y coord_system x_basket_m
#>       <int>      <int>  <int>   <int> <int> <int> <chr>             <dbl>
#>  1   105401         91   2027     901  1426  -673 mm                1.43 
#>  2   105401         91   2027    1001   484   698 mm                0.484
#>  3   105401         91   2027    1701  1452  -806 mm                1.45 
#>  4   105401         91   2027    1801  3308   -27 mm                3.31 
#>  5   105401         91   2027    1901  1183   565 mm                1.18 
#>  6   105401         91   2027    2001   538  -592 mm                0.538
#>  7   105401         91   2027    2101  1291   915 mm                1.29 
#>  8   105401         91   2027    2201  2125  -646 mm                2.12 
#>  9   105401         91   2027    2701  4815   -53 mm                4.82 
#> 10   105401         91   2027    2901  8150 -4302 mm                8.15 
#> # ℹ 178 more rows
#> # ℹ 16 more variables: y_basket_m <dbl>, quarter <int>, minute <int>,
#> #   second <int>, clock_seconds <int>, play_type <int>, play_name <chr>,
#> #   is_home <lgl>, score_home <int>, score_away <int>, player_id <int>,
#> #   player_name <chr>, shirt_number <chr>, zone <chr>, made <lgl>, points <int>
# }