Skip to contents

Play-by-play: shots, faceoffs, hits, blocks, giveaways, takeaways, penalties and goals (2008-09 on).

Usage

khl_pbp(
  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 (26 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)
event_indexinteger0-based order of the event in the game, oldest first
event_typecharacterperiod (start / end marker), faceoff, shot, block, action, penalty or goal
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
resultcharacterEvent result code (shots: goal / saved / miss; actions: hit, gva = giveaway, tka = takeaway, intc = interception)
result_textcharacterEvent result label
penalty_minutesintegerPenalty minutes (penalty events)
score_aintegerHome goals after the goal
score_bintegerVisitor goals after the goal
won_sidecharacterFaceoff winner's side (A / B)
won_jnintegerFaceoff winner's jersey
won_player_idintegerFaceoff winner's khl.ru player id
lost_sidecharacterFaceoff loser's side (A / B)
lost_jnintegerFaceoff loser's jersey
lost_player_idintegerFaceoff loser's khl.ru player id
won_zonecharacterZone of the faceoff from the winner's view: offensive, neutral or defensive
shooter_sidecharacterBlocked shooter's side (A / B)
shooter_jnintegerBlocked shooter's jersey
shooter_player_idintegerBlocked shooter's khl.ru player id
period_actioncharacterPeriod marker: 0 = start, 1 = end (inferred)

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_pbp(game_id = 898236, season = 2026))
#> ── KHL Play-by-Play from khl.ru ────────────────────── fastRhockey 1.0.0.9000 ──
#> ℹ Data updated: 2026-10-13 01:54:43 UTC
#> # A tibble: 268 × 26
#>    game_id tournament_id period event_index event_type time  time_seconds
#>      <int>         <int>  <int>       <int> <chr>      <chr>        <int>
#>  1  898236          1369      1           0 period     19:31           NA
#>  2  898236          1369      1           1 faceoff    00:00            0
#>  3  898236          1369      1           2 action     00:04            4
#>  4  898236          1369      1           3 action     00:19           19
#>  5  898236          1369      1           4 action     00:38           38
#>  6  898236          1369      1           5 action     00:45           45
#>  7  898236          1369      1           6 action     01:54          114
#>  8  898236          1369      1           7 action     02:05          125
#>  9  898236          1369      1           8 action     02:05          125
#> 10  898236          1369      1           9 action     02:11          131
#> # ℹ 258 more rows
#> # ℹ 19 more variables: team_side <chr>, jn <int>, player_id <int>,
#> #   result <chr>, result_text <chr>, penalty_minutes <int>, score_a <int>,
#> #   score_b <int>, won_side <chr>, won_jn <int>, won_player_id <int>,
#> #   lost_side <chr>, lost_jn <int>, lost_player_id <int>, won_zone <chr>,
#> #   shooter_side <chr>, shooter_jn <int>, shooter_player_id <int>,
#> #   period_action <chr>
# }