NHL API in Python: scores, goals and injuries

A single Python script with three commands: a day's NHL games with scores and event ids, every goal in one game with the score after it, and the league-wide or per-team injury report.

Stack
Python, requests
Plan
Free (goals reads every play page, typically 3 calls per game)
Code
python-nhl/ (MIT)

Updated 2026-11-05. Tested against the live API on that date. Data is aggregated from public sources and is typically 20-30 seconds behind live play (about 1 second after our source). There is no SLA.

1. Get a free API key

Sign up (no card) and copy the key from the dashboard. The Free plan gives you 1,000 calls in the first 30 days, then 125 a month, at 1 request per second. Every successful REST call counts as one call.

Check the key works with one call to the live-scores endpoint:

Terminal

export REALTIME_SPORTS_API_KEY=your_key
curl -H "Authorization: Bearer $REALTIME_SPORTS_API_KEY" \
  "https://www.realtimesportsapi.com/api/v1/sports/football/leagues/nfl/events/live"

2. Get the code

Everything below is in the python-nhl/ folder of the examples repo (MIT). Clone it and work from that folder.

Terminal

git clone https://github.com/ElcoDevRepos/realtime-sports-api-examples
cd realtime-sports-api-examples/python-nhl

3. Install

Python 3.10+ and requests. Nothing else.

Terminal

python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env    # REALTIME_SPORTS_API_KEY

4. The script

scores reads today's scoreboard, or any day with --date. goals reads every page of the play-by-play and keeps the scoring plays, printing the period, clock and score after each goal. injuries prints the injury report, league-wide or for one team with --team.

python-nhl/nhl.py

#!/usr/bin/env python3
"""NHL data in Python with the Realtime Sports API: scores, goals and injury reports.

Usage:
  python nhl.py scores                      # today's scoreboard
  python nhl.py scores --date 2026-11-04    # any day
  python nhl.py goals <eventId>             # every goal in a game, with the score after it
  python nhl.py injuries [--team <id>]      # injury report, league-wide or one team

Needs REALTIME_SPORTS_API_KEY (env or .env). For goal alerts pushed to you, see the nhl-goal-alerts example.
"""
from __future__ import annotations

import argparse
import os
import sys
from pathlib import Path
from typing import Any, Dict

import requests

API_BASE = os.environ.get("RSA_API_BASE", "https://www.realtimesportsapi.com/api/v1")
NHL = "/sports/hockey/leagues/nhl"


def load_dotenv(path: Path) -> None:
    """Tiny .env loader (KEY=value). Existing environment variables win."""
    if not path.exists():
        return
    for line in path.read_text().splitlines():
        line = line.strip()
        if line and not line.startswith("#") and "=" in line:
            k, v = line.split("=", 1)
            os.environ.setdefault(k.strip(), v.strip().strip('"').strip("'"))


def api_get(path: str, **params: Any) -> Dict[str, Any]:
    key = os.environ.get("REALTIME_SPORTS_API_KEY")
    if not key:
        sys.exit("Set REALTIME_SPORTS_API_KEY (get a free key at https://www.realtimesportsapi.com/signup)")
    r = requests.get(f"{API_BASE}{path}", params=params, headers={"Authorization": f"Bearer {key}"}, timeout=20)
    body = r.json()
    if r.status_code != 200 or not body.get("success", False):
        err = body.get("error") or {}
        sys.exit(f"HTTP {r.status_code}: {err.get('message') or body}")
    return body


def team_line(t: Dict[str, Any]) -> str:
    score = t.get("score")
    return f"{t.get('abbreviation') or t.get('name')}{'' if score is None else f' {score}'}"


def print_games(games) -> None:
    for g in sorted(games, key=lambda e: e.get("date") or ""):
        st = g.get("status") or {}
        print(f"[{g['id']}] {g.get('date', '')[:16]}  {team_line(g['awayTeam'])} @ {team_line(g['homeTeam'])}  {st.get('detail') or st.get('state')}")


def cmd_scores(args: argparse.Namespace) -> None:
    params = {"date": args.date} if args.date else {}
    print_games(api_get(f"{NHL}/events", **params)["data"])


def cmd_goals(args: argparse.Namespace) -> None:
    path = f"{NHL}/events/{args.event_id}/plays"
    first = api_get(path, page=1, limit=100)
    plays = list(first["data"])
    for page in range(2, first["meta"]["pagination"]["totalPages"] + 1):
        plays += api_get(path, page=page, limit=100)["data"]
    goals = [p for p in plays if p.get("scoringPlay")]
    for p in goals:
        clock = (p.get("clock") or {}).get("displayValue", "")
        print(f"P{p.get('period')} {clock:>5}  {p.get('awayScore')}-{p.get('homeScore')}  {p.get('text')}")
    print(f"\n{len(goals)} goals")


def cmd_injuries(args: argparse.Namespace) -> None:
    params = {"team": args.team} if args.team else {}
    data = api_get(f"{NHL}/injuries", **params)["data"] or {}
    for group in data.get("groups") or []:
        rows = group.get("injuries") or []
        if not rows:
            continue
        t = rows[0].get("team") or group.get("team") or {}
        print(f"\n{t.get('displayName') or 'Team ' + str(t.get('id', '?'))} ({len(rows)})")
        for i in rows:
            ath = i.get("athlete") or {}
            print(f"  {ath.get('displayName')} ({ath.get('position')}): {i.get('status')}")
    print(f"\n{data.get('count', 0)} records")


def main() -> None:
    load_dotenv(Path(__file__).with_name(".env"))
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    sub = ap.add_subparsers(dest="cmd", required=True)
    s = sub.add_parser("scores"); s.add_argument("--date"); s.set_defaults(fn=cmd_scores)
    g = sub.add_parser("goals"); g.add_argument("event_id"); g.set_defaults(fn=cmd_goals)
    i = sub.add_parser("injuries"); i.add_argument("--team"); i.set_defaults(fn=cmd_injuries)
    args = ap.parse_args()
    args.fn(args)


if __name__ == "__main__":
    main()

5. Run it

On 5 November 2026, scores --date 2026-11-04 listed [401892644] OTT 2 @ BUF 1 Final, and goals 401892644 ended with P3 16:51 2-1 Michael Amadio Goal (5) Snap Shot and 3 goals. injuries returned 124 records.

Terminal

python nhl.py scores --date 2026-11-04   # a day's games, with event ids
python nhl.py goals 401892644            # every goal in the game
python nhl.py injuries                   # league-wide injury report

6. Going further

For goals pushed to you as they happen, see NHL goal alerts and the Home Assistant guide. Every NHL endpoint is on the NHL API page, with play-by-play and injuries. Data typically runs 20-30 seconds behind live play.

Endpoints used

  • GET /sports/hockey/leagues/nhl/events
  • GET /sports/hockey/leagues/nhl/events/{eventId}/plays
  • GET /sports/hockey/leagues/nhl/injuries

Full reference: docs · OpenAPI · what each endpoint returns, by league

FAQ

Is there a free NHL API for Python?
A free key (no card) gives 1,000 calls in the first 30 days, then 125 a month, and includes every endpoint. This guide uses requests; no wrapper library needed.
How do I get NHL goals for a game?
Read GET /sports/hockey/leagues/nhl/events/{eventId}/plays page by page and keep plays where scoringPlay is true. Each has the period, clock, text and the score after the play.
Is this the NHL's own API?
No. It's a documented third-party API with one key for the NHL and every other league we cover, so the same code pattern works for the NBA, NFL or soccer.

More guides