Skip to contents

fastRhockey 1.0.0 (continued development)

New features

  • Added read-only Fox Sports “Bifrost” NHL wrappers (fox_nhl_*) over api.foxsports.com/bifrost/v1/nhl/*, complementing the espn_nhl_* family: fox_nhl_pbp() (period-based play-by-play), fox_nhl_boxscore(), fox_nhl_odds(), fox_nhl_team_roster(), fox_nhl_team_stats(), fox_nhl_team_gamelog(), fox_nhl_standings(), fox_nhl_league_leaders(). Flatten Fox’s layout JSON into tidy fastRhockey_data tibbles. Parallels the cfbfastR / hoopR / sportsdataverse-py fox_* families; reverse-engineering notes + an OpenAPI 3.1 spec live in the sdv-internal-refs repo.
  • Added live HockeyTech wrappers for AHL, OHL, WHL, and QMJHL (<lg>_schedule(), <lg>_pbp(), <lg>_standings(), <lg>_teams(), <lg>_team_roster(), <lg>_player_stats(), <lg>_leaders(), <lg>_game_summary(), <lg>_season_id(), most_recent_<lg>_season()).
  • Added on-ice / Corsi-Fenwick / TOI analytics across all five HockeyTech leagues (*_game_shifts(), *_player_toi(), *_game_corsi()). Corsi/Fenwick are proxies — the feed has no missed-shot event (corsi_includes_missed).
  • pwhl_pbp() now returns a superset: added shot_distance, shot_angle, scoring_chance, on_ice_home, on_ice_away, coordinate-transform + clock columns, and blocked_shot/hit events.

ESPN NHL endpoints

  • Added a comprehensive family of ESPN NHL endpoint wrappers (espn_nhl_*, 125 functions) covering the full ESPN hockey API surface across the site, web, and core hosts — game data (espn_nhl_pbp, espn_nhl_team_box, espn_nhl_player_box, espn_nhl_game_all, espn_nhl_summary, espn_nhl_scoreboard, espn_nhl_schedule, espn_nhl_standings), athletes, leaders, season structure, event detail, and reference catalog. Each returns a wide, self-describing fastRhockey_data tibble with a documented column table, built on a shared league-generic helper layer. Endpoints ESPN does not populate for the NHL degrade gracefully to an empty frame with a warning.

Data loaders

Dependencies

  • Migrated the entire package from httr to httr2 behind a single internal .retry_request() helper, and added session / environment / per-call proxy support via options(fastRhockey.proxy = ...) (documented in ?fastRhockey). Function outputs are unchanged.
  • Raised the minimum R version to 4.1.0 (the new httr2 request helpers use the native |> pipe).

Deprecations & fixes

CI: NHL loader + xG tests, and two fixes

  • Fix: nhl_schedule(include_data_flags = TRUE) failed with `x` and `y` must share the same src and returned a schedule with no flag columns. load_nhl_games() returns a data.table, so the flag left-join was being routed through the dtplyr backend, and the data.table [, <character>] subset evaluated to the literal column names rather than selecting columns. The index is now coerced to a tibble before the join, so the 16 data-availability flag columns attach correctly.
  • Fix: NHL Edge “top-10” leaderboard endpoints the NHL has removed return HTTP 404. The Edge fetch helper now passes terminate_on to httr::RETRY so permanent client errors (400/401/403/404/410/422) are not retried — the functions still return NULL gracefully, but without the wasteful retry loop and log noise. Transient 5xx/timeouts are still retried.
  • Test infrastructure: test-load_nhl.R and test-calculate_xg.R no longer skip_on_ci(), so the NHL loaders and the xG pipeline are exercised on CI (xgboost is installed explicitly in the workflow). Loader tests now request seasons that are actually published per table and assert on the keys each release carries.

Documentation: @return column tables for all working functions

  • Every working exported function (165 across the NHL, NHL Edge, NHL Records, PWHL, and loader families) now documents its return value with a col_name | types | description table, matching the convention used in hoopR, wehoop, and cfbfastR. Functions that return a named list of data frames get one table per frame. Column names and types were captured from live API responses, so the tables reflect the actual returned schema.

nhl_schedule() gains a game_type parameter

  • nhl_schedule() gains a game_type parameter accepting "both" (default), "regular", or "playoffs". In season mode the function now returns regular-season and playoff games in a single tibble. Three new columns (series_letter, playoff_round, series_game_number) are appended to the output; they are NA on regular-season rows.
  • Behavior change: nhl_schedule(season = ...) previously returned only what the club-schedule-season endpoint provided, which mixed preseason (PR), regular (R), and playoff (P) rows. With this release:
    • game_type defaults to "both", so playoff games are now included by default. Pass game_type = "regular" to restore regular-only behavior.
    • Preseason (PR) rows are no longer returned by season-mode calls regardless of game_type. Use nhl_schedule(day = ...) to retrieve preseason game schedules.
  • nhl_playoff_schedule() is unchanged in its public API; internally it now delegates its HTTP call to a new shared internal helper .fetch_playoff_series().
  • Fix: nhl_schedule() previously returned the same home_team_name, away_team_name, home_score, away_score, and venue value in every row regardless of the actual game. Root cause was an ifelse(scalar, vec, NA) guard in .parse_schedule_games() and .parse_club_schedule_games() that collapsed each vector to length 1, which tibble then recycled across all rows. Replaced with if/else, and added regression tests asserting per-row distinct values. home_team_abbr, away_team_abbr, and the other directly-assigned columns were not affected.
  • Hardening: nhl_player_info() used the same ifelse(scalar, vec, NA) idiom across all 18 of its nullable fields. The function returns a 1-row tibble per player so the bug was masked today, but the latent shape was identical to the nhl_schedule() bug above. Replaced with if/else for consistency and to remove the foot-gun before it surfaces (e.g., if the function is ever refactored to handle multiple players).

pwhl_schedule() defaults to combined regular season + playoffs

  • pwhl_schedule() gains a game_type parameter accepting "both" (default), "regular", "playoffs", or "preseason". A new game_type column is appended labeling each row.
  • Behavior change: game_type now defaults to "both", so a call such as pwhl_schedule(season = 2024) returns regular-season and playoff games in a single, chronologically ordered data frame. Pass game_type = "regular" to restore the previous regular-only behavior. Preseason is excluded from "both"; request it with game_type = "preseason".
  • When a requested game_type does not yet exist for a season (e.g. playoffs for an in-progress season), it is skipped rather than erroring, so "both" still returns the regular-season games that do exist.
  • The HockeyTech schedule feed exposes no machine-readable date, so rows are ordered by a date parsed locale-independently from the feed’s "date_with_day" display string.
  • Fix: pwhl_schedule()’s winner column is now derived from game_status and an integer comparison of the goal counts. Previously the character goal counts were compared with >, so a double-digit score sorted lexically ("10" < "9") and could name the wrong winner; games that have not been played now yield winner = NA (the prior code emitted a "-" placeholder). PWHL games cannot end in a regulation tie, so the former "Tie" branch is gone.

pwhl_player_game_log() and pwhl_streaks() default to combined types

  • pwhl_player_game_log() and pwhl_streaks() gain the same game_type parameter ("both" default, plus "regular", "playoffs", "preseason") and a game_type column labeling each row. With "both", a player’s regular-season and playoff data are returned together. A game type the player/season has no data in (e.g. playoffs for a non-qualifying team) is skipped rather than erroring, so the call still returns the data that does exist.
  • pwhl_transactions() is intentionally unchanged: transactions are not scoped to a game type (the playoff season feed carries none), so a combined view would add no information.

PWHL parity: 3 new NHL loaders + datasets

Three new season-level NHL loaders that bring NHL coverage in line with PWHL. Each is backed by a new release tag on sportsdataverse/sportsdataverse-data and powered by new extractors in fastRhockey-nhl-raw/R/scrape_nhl_raw.R and fastRhockey-nhl-data/R/nhl_data_creation.R.

function release tag rows per game
load_nhl_officials() nhl_officials one per official (refs+lins)
load_nhl_shots_by_period() nhl_shots_by_period one per team per period
load_nhl_shootout() nhl_shootout one per shootout attempt

nhl_schedule() data-availability flags

nhl_schedule() gained an include_data_flags = FALSE parameter. When TRUE, the live schedule is left-joined against the cached nhl_games_in_data_repo index from the data repo and gains 16 logical columns (PBP, team_box, player_box, skater_box, goalie_box, game_info, game_rosters, scoring, penalties, scratches, linescore, three_stars, shifts, officials, shots_by_period, shootout) telling you which pre-compiled datasets cover each game. The schedule files in fastRhockey-nhl-raw/nhl/schedules/ and the master schedule on the data repo carry these same flags.

Richer per-game JSON in fastRhockey-nhl-raw

scrape_nhl_raw.R now writes four additional top-level keys into both nhl/json/raw/{game_id}.json and nhl/json/final/{game_id}.json:

  • officials — list of {role, name} (referees first, linesmen second)
  • shots_by_period — long-form per-team-per-period shot totals
  • shootout — per-attempt summary (NULL if game didn’t reach SO)
  • plays_by_period — OLD-format index of play_indices per period
  • on_ice / on_ice_plus / penalty_box (final-state reconstruction, derived from the processed PBP) — populated only in the final/ variant since they need fastRhockey-enriched on-ice columns

Refactor: NHL loaders consolidated into R/nhl_loaders.R

All NHL season-level loaders and the shared .nhl_release_loader() helper now live in R/nhl_loaders.R, mirroring the PWHL convention (R/pwhl_loaders.R). The empty R/nhl_pbp.R file was removed since it never contained an actual nhl_pbp() scraper — only loaders. Public signatures and return types are unchanged.

New NHL Edge Analytics (33 functions)

Wraps the NHL Edge advanced-metrics endpoints under https://api-web.nhle.com/v1/edge/.... Every wrapper accepts an optional season argument (4-digit end year, e.g., 2025) — when omitted, the /now form is used to fetch the current season. All wrappers share an internal .nhl_edge_api() helper in R/helpers_nhl_edge.R.

family functions
Skater nhl_edge_skater_detail(), nhl_edge_skater_landing(), nhl_edge_skater_comparison(), nhl_edge_skater_shot_location_detail(), nhl_edge_skater_shot_location_top_10(), nhl_edge_skater_shot_speed_detail(), nhl_edge_skater_shot_speed_top_10(), nhl_edge_skater_skating_speed_detail(), nhl_edge_skater_speed_top_10(), nhl_edge_skater_skating_distance_detail(), nhl_edge_skater_distance_top_10(), nhl_edge_skater_zone_time(), nhl_edge_skater_zone_time_top_10(), nhl_cat_edge_skater_detail()
Goalie nhl_edge_goalie_detail(), nhl_edge_goalie_landing(), nhl_edge_goalie_comparison(), nhl_edge_goalie_5v5_detail(), nhl_edge_goalie_5v5_top_10(), nhl_edge_goalie_save_percentage_detail(), nhl_edge_goalie_edge_save_pctg_top_10(), nhl_edge_goalie_shot_location_detail(), nhl_edge_goalie_shot_location_top_10(), nhl_cat_edge_goalie_detail()
Team nhl_edge_team_detail(), nhl_edge_team_landing(), nhl_edge_team_shot_location_detail(), nhl_edge_team_shot_location_top_10(), nhl_edge_team_shot_speed_detail(), nhl_edge_team_skating_speed_detail(), nhl_edge_team_skating_speed_top_10(), nhl_edge_team_skating_distance_detail(), nhl_edge_team_skating_distance_top_10(), nhl_edge_team_zone_time_details(), nhl_edge_team_zone_time_top_10()

New NHL Stats REST wrappers (13 functions)

Promotes endpoints that were previously only reachable via nhl_stats_misc()’s generic dispatcher into discoverable, dedicated wrappers. Documentation for nhl_stats_misc() was also updated to enumerate every valid endpoint value.

New NHL api-web miscellaneous endpoints (6 functions + 3 in-place updates)

  • nhl_wsc_pbp(game_id) – narrative-format play-by-play from wsc/play-by-play/{gameId} (distinct from nhl_game_pbp() which uses gamecenter/{id}/play-by-play).
  • nhl_draft_tracker() – live draft tracker (draft-tracker/picks/now), distinct from nhl_draft() which hits the static draft/picks/now endpoint.
  • nhl_ppt_replay(game_id, event_number) – event-level replay metadata.
  • nhl_ppt_replay_goal(game_id, event_number) – goal-specific replay metadata.
  • nhl_postal_lookup(postal_code) – broadcast region lookup by postal code.
  • nhl_smartlinks(handle = NULL) – NHL.com smart-link router.
  • nhl_scoreboard(date = NULL) – now accepts an optional date argument so callers can fetch historical scoreboards (was hardcoded to /now).
  • nhl_meta(game_id = NULL, year = NULL, series_letter = NULL) – added a third branch for meta/playoff-series/{year}/{seriesLetter}.
  • nhl_draft_year(year, round = NULL) – when round is NULL or "all", the function now hits the /draft/picks/{year}/all shortcut in a single request instead of looping per round.

New helper aggregators inspired by nhl-api-py (6 functions)

Convenience wrappers that orchestrate multiple endpoint calls into one tidy data frame:

  • nhl_game_ids_by_season(season, game_types, team_abbr, sleep_rate) – iterates every team’s season schedule and returns the deduplicated set of game IDs.
  • nhl_all_players_by_season(season, sleep_rate) – iterates every team’s roster and flattens forwards/defensemen/goalies.
  • nhl_player_career_stats(player_id) – combines player/{id}/landing with the season-by-season seasonTotals payload into a career frame.
  • nhl_team_summary_range(start_season, end_season) – multi-season team summary loop over nhl_stats_teams(report_type = "summary").
  • nhl_skater_summary_range(start_season, end_season) – same for skater summary.
  • nhl_goalie_summary_range(start_season, end_season) – same for goalie summary.

Endpoint mapping + OpenAPI specs

  • data-raw/nhl_missing_endpoint_function_mapping.md – table of every documented NHL API endpoint and its proposed/implemented fastRhockey wrapper, sourced from dfleis/nhl-api-docs, RentoSaijo/nhlscraper, and coreyjs/nhl-api-py.
  • data-raw/nhl_api_web_openapi.{json,yaml} – OpenAPI 3.0.3 spec for api-web.nhle.com/v1/ (132 endpoints).
  • data-raw/nhl_stats_rest_openapi.{json,yaml} – OpenAPI 3.0.3 spec for api.nhle.com/stats/rest/{lang}/ (21 endpoints).
  • data-raw/nhl_records_openapi.{json,yaml} – OpenAPI 3.0.3 spec for records.nhl.com/site/api/ (442 endpoints).
  • data-raw/_gen_openapi.py – regenerator script.

Bug Fixes / API quirks documented

  • nhl_stats_players() now detects data: [] empty-list responses (the API returns this when no cayenne_exp filter is supplied) and returns NULL with a friendly hint instead of crashing clean_names().
  • nhl_stats_skater_leaders() and nhl_stats_goalie_leaders() no longer pass start/limit query parameters, which the leaders/{skaters,goalies}/{attribute} endpoint rejects with a 500 Cayenne SQL error. Roxygen now documents the valid attribute values: skaters accept assists/goals/points; goalies accept savePctg/gaa/shutouts.
  • nhl_records_franchise(franchise_id = ...) now translates the franchise_id argument into a cayenneExp=id={id} filter. The records API does not support a path-suffix franchise/{id} form (returns 404).
  • nhl_records_award_details() – replaced the broken franchise_id argument with season_id (the award-details endpoint accepts seasonId filtering but rejects franchiseId).
  • nhl_stats_content_module() now guards against unnamed CMS responses that previously crashed clean_names().

New NHL Loaders

Ten new season-level loaders that pull pre-compiled NHL datasets from new release tags on sportsdataverse/sportsdataverse-data. All accept a seasons vector (Min: 2011) and return a fastRhockey_data data frame, mirroring the existing load_nhl_*() API:

function release tag rows per game
load_nhl_skater_box() nhl_skater_boxscores one per skater
load_nhl_goalie_box() nhl_goalie_boxscores one per goalie
load_nhl_game_rosters() nhl_game_rosters one per dressed player
load_nhl_game_info() nhl_game_info one
load_nhl_scoring() nhl_scoring one per goal
load_nhl_penalties() nhl_penalties one per penalty
load_nhl_three_stars() nhl_three_stars up to three
load_nhl_scratches() nhl_scratches one per scratched player
load_nhl_linescore() nhl_linescore one
load_nhl_shifts() nhl_shifts one per shift

The six pre-existing loaders (load_nhl_pbp(), load_nhl_pbp_lite(), load_nhl_player_box(), load_nhl_team_box(), load_nhl_schedule(), load_nhl_rosters()) were refactored to share a single internal worker (.nhl_release_loader()) without changing their public signatures or return types.

New PWHL Loaders

Eleven new season-level loaders that pull pre-compiled PWHL datasets from new release tags on sportsdataverse/sportsdataverse-data. All accept a seasons vector (Min: 2024) and return a fastRhockey_data data frame, mirroring the existing load_pwhl_*() API:

function release tag rows per game
load_pwhl_skater_box() pwhl_skater_boxscores one per skater
load_pwhl_goalie_box() pwhl_goalie_boxscores one per goalie
load_pwhl_team_box() pwhl_team_boxscores two (home/away)
load_pwhl_game_info() pwhl_game_info one
load_pwhl_scoring_summary() pwhl_scoring_summary one per goal
load_pwhl_penalty_summary() pwhl_penalty_summary one per penalty
load_pwhl_three_stars() pwhl_three_stars up to three
load_pwhl_officials() pwhl_officials one per official
load_pwhl_shots_by_period() pwhl_shots_by_period one per period
load_pwhl_shootout() pwhl_shootout one per attempt
load_pwhl_game_rosters() pwhl_game_rosters one per dressed player

The four pre-existing loaders (load_pwhl_pbp(), load_pwhl_player_box(), load_pwhl_schedule(), load_pwhl_rosters()) were refactored to share a single internal worker (.pwhl_release_loader()) without changing their public signatures or return types.

fastRhockey 1.0.0

Breaking Changes

  • Version 1.0.0 signals API stability for NHL and PWHL endpoints.
  • All PHF functions are formally deprecated via lifecycle. The Premier Hockey Federation ceased operations; use PWHL functions instead. Functions will be removed in a future release.
  • Consolidated new NHL API functions into existing NHL function names rather than creating _v2 variants, since the original API endpoints were deprecated by the NHL. This means nhl_game_feed(), nhl_game_boxscore(), nhl_schedule(), nhl_teams(), nhl_teams_roster(), and nhl_player_info() now use the new api-web.nhle.com endpoints directly.

New PWHL Functions

Bug Fixes

  • Fixed nhl_draft_year() – NHL API removed the /v1/draft/picks/{year} endpoint. The function now iterates over rounds 1-7 using /v1/draft/picks/{year}/{round}.
  • Fixed pwhl_stats() – resolved “object ‘players’ not found” error when API calls failed. Fixed team ID resolution for skater stats.
  • Fixed pwhl_schedule() – the season column was inadvertently dropped from the output. It is now included.
  • Fixed refresh_xg_models() – resolved “cannot change value of locked binding” error by storing xG models in a package environment (.xg_env) instead of top-level bindings.
  • Fixed NAMESPACE: removed import(tidyverse) which violated CRAN policy. Individual packages (dplyr, tidyr, etc.) are already imported.

Improvements

  • pwhl_season_id() now retrieves season data dynamically from the HockeyTech API instead of using a hardcoded lookup table. Falls back to hardcoded data when the API is unavailable.
  • Added internal helpers .pwhl_api(), .pwhl_modulekit_url(), .pwhl_gc_url(), and .pwhl_resolve_season_id() to reduce JSONP parsing boilerplate across PWHL functions.
  • Added lifecycle package for formal deprecation management.
  • Updated testthat dependency to >= 3.0.0.
  • Complete test coverage for all 95 exported functions (482 tests).
  • Added environment-controlled test toggles (RUN_NHL_TESTS, RUN_PHF_TESTS, RUN_PWHL_TESTS) via tests/testthat/helper-skip.R.
  • CI workflow environment variables now match helper-skip.R names.
  • Updated test expectations to match current API responses.
  • nhl_where_to_watch() returns NULL gracefully when the NHL /v1/where-to-watch endpoint is unavailable.
  • Updated _pkgdown.yml with reorganized reference sections and deprecated function categories.

Documentation & Infrastructure

  • Updated CONTRIBUTING.md with naming conventions, testing environment variables, conventional commits guide, and deprecation process.
  • Updated PR and issue templates.
  • Updated CLAUDE.md and .github/copilot-instructions.md to reflect v1.0.0 changes and new PWHL endpoints.
  • Added data-raw/pr_devel.md development scratchpad.
  • Added cran_comments.md for CRAN submission.
  • Updated _pkgdown.yml with reorganized PWHL reference sections.
  • Added R CMD check CI workflow and pkgdown deployment workflow.

fastRhockey 0.7.0

PWHL functions added

fastRhockey 0.6.0

  • Improved resiliency for several PHF functions, updates under the hood.

fastRhockey 0.5.0

  • Major improvements to NHL Game PBP Data parsing with shifts in-line via nhl_game_pbp() function added to match hockeyR.

fastRhockey 0.4.2

fastRhockey 0.4.1

  • Minor logic addition for pbp parsing.
  • More under the hood changes to adapt to tidyselect new version guidelines.
  • load_phf_rosters() function added.
  • load_nhl_rosters() function added.

fastRhockey 0.4.0

CRAN release: 2022-10-25

  • Updates logic to add Montreal Force to teams lists/parsing.
  • Under the hood changes to adapt to tidyselect new version guidelines.

fastRhockey 0.3.1

CRAN release: 2022-08-28

  • Updates documentation per CRAN’s request.

fastRhockey 0.3.0

CRAN release: 2022-03-25

  • Add print method for all functions with a time stamp and description of the data.
  • Add phf_team_logos dataset to package for reference.

fastRhockey 0.2.1

  • hotfix to helper_phf_pbp_data() penalty code.
  • add try() to function examples.

fastRhockey 0.2.0

fastRhockey 0.1.0

CRAN release: 2021-12-10

  • Prepped for CRAN.

fastRhockey 0.0.4

Loader functions for PHF

Player and Team Stats, Leaderboards

fastRhockey 0.0.3

Function naming convention normalization

fastRhockey 0.0.2

  • Added NHL functions from powerplay to fastRhockey.

fastRhockey 0.0.1

  • Added a NEWS.md file to track changes to the package.