Skip to contents

Shot map: one row per shot with its x / y on the 1000 x 500 rink grid (2024-25 on).

Usage

khl_shots(
  game_id,
  season = NULL,
  stage = "regular",
  headers = NULL,
  tournament_id = NULL,
  return_parsed = TRUE
)

Arguments

game_id

Required. khl.ru game id (the schedule's game_id; equals webcaster event khl_id).

season

Season by its END year (2026 = 2025-26), mapped to the KHL tournament id (tnId) through the webcaster season table (khl_webcaster_stages). The game's season; required unless tournament_id is given.

stage

Which stage of the season: regular or playoff. One of regular, playoff.

headers

Named list of extra request headers, merged over the defaults (X-Requested-With, Origin, Referer). The cookie jar and sessid are kept in-process, so nothing needs reusing across calls.

tournament_id

khl.ru tournament id (tnId), sent as is instead of season / stage (1369 = 2025-26 regular season, 1370 its playoff; the schedule's tournament_id column). The All-Star games have their own.

return_parsed

Logical. TRUE (default) returns the parsed fastRhockey_data tibble; FALSE returns the raw khl.ru envelope as a list (status, data, errors; betting fields removed).

Value

A fastRhockey_data tibble (15 columns; zero rows with the same columns when the payload is empty), or the raw body as a list when return_parsed = FALSE. Raises fastRhockey_no_data (nothing there, or an unknown season / stage), fastRhockey_asset_fetch (an error envelope, an answer for another id than the one asked, or a failed fetch) and fastRhockey_bad_request (an invalid argument or an HTTP 400 / 422).

col_nametypesdescription
game_idintegerkhl.ru game id; equals webcaster event khl_id / match_id
tournament_idintegerkhl.ru tournament (stage) id, tnId: 1369 = 2025-26 regular season, 1370 its playoff; equals webcaster stage khl_id
periodintegerPeriod number (box score: 0 = the whole game; 4+ = overtime)
timecharacterGame clock mm:ss (game-elapsed); period marker rows keep their wall-clock HH:MM
time_secondsintegerGame-elapsed seconds (null on period marker rows)
team_sidecharacterA = home, B = visitor
jnintegerJersey number (not an id; 0 on a team event)
player_idintegerkhl.ru player id; equals webcaster player khl_id
shot_typeinteger1 = goal, 2 = saved (inferred upstream; unverified)
xintegerShot x on the site's 1000-wide rink grid (indent_left = x/10 %); whether shots are normalised to one attacking direction is UNVERIFIED
yintegerShot y on the site's 500-high rink grid (indent_top = y/5 %); orientation UNVERIFIED
indent_leftnumericShot position as percent from the left edge
indent_topnumericShot position as percent from the top edge
indent_rightnumericShot position as percent from the right edge
indent_bottomnumericShot position as percent from the bottom edge

Details

Endpoint: POST https://www.khl.ru/rest/game/text/.

khl.ru REST needs a primed session (a GET of the site root sets the cookie jar and the Bitrix sessid, then each call POSTs a form); this is done for you, one session per host per R session, re-primed when the site rotates its cookie or the sessid expires. No route takes a year: season (the END year, 2026 = 2025/26) and stage are mapped to the tournament id (tnId) through the webcaster season table, or pass tournament_id (the All-Star games have their own tnId).

Ids and counts are integers (sdv-py Int64); a column holding values past 2^31 (epoch milliseconds) is returned as a whole-number double.

At least one second is left between two requests to a host.

Owner flag (sanctions). The KHL and its webcaster platform are run by Russian operators, some clubs by sanctioned state companies. Fetching on your own machine is fine; any published data release, hosted mirror or redistribution needs owner (sanctions / legal) sign-off first.

Examples

# \donttest{
  try(khl_shots(game_id = 898236, season = 2026))
#> ── KHL Shot Map from khl.ru ────────────────────────── fastRhockey 1.0.0.9000 ──
#> ℹ Data updated: 2026-10-13 01:54:59 UTC
#> # A tibble: 69 × 15
#>    game_id tournament_id period time  time_seconds team_side    jn player_id
#>      <int>         <int>  <int> <chr>        <int> <chr>     <int>     <int>
#>  1  898236          1369      1 02:23          143 A            94     17624
#>  2  898236          1369      1 02:49          169 A            94     17624
#>  3  898236          1369      1 04:16          256 B            65     35032
#>  4  898236          1369      1 05:11          311 B            72     19216
#>  5  898236          1369      1 05:17          317 A            87     29928
#>  6  898236          1369      1 05:25          325 B            29     27904
#>  7  898236          1369      1 06:09          369 B             5     32390
#>  8  898236          1369      1 07:32          452 B            61     40421
#>  9  898236          1369      1 08:44          524 B            65     35032
#> 10  898236          1369      1 11:01          661 A            16     40673
#> # ℹ 59 more rows
#> # ℹ 7 more variables: shot_type <int>, x <int>, y <int>, indent_left <dbl>,
#> #   indent_top <dbl>, indent_right <dbl>, indent_bottom <dbl>
# }