Growing Discord community — direct access to the developer, live coverage & picks. Join the Discord Join now →
Matches Leagues Predictions The Edge Money News Stats Coverage
Docs / More Sports APIs pro

Hockey API#

The Hockey API covers ice hockey (NHL and international leagues): leagues, teams with ELO ratings, matches with period-by-period scores, overtime/shootout flags, decimal odds and model-based predictions.

Base URL: https://sports.bzzoiro.com/hockey/api/v2/

Access#

Requires the Sports Addon ($5/month) — it unlocks the tennis, CS2, darts, hockey, basketball and horse racing APIs plus their MCP servers. Get it at /addons/. You also need a free account token from /register/.

Authorization: Token YOUR_API_KEY

Without a token you get 401:

{"error": "authentication required", "code": "authentication_required",
 "detail": "Send Authorization: Token <YOUR_TOKEN> on every request.",
 "register": "https://sports.bzzoiro.com/register/"}

With a token but no addon you get 402:

{"error": "Sports Addon required", "code": "addon_required",
 "detail": "Tennis, CS:GO, darts, hockey, basketball and horse racing APIs require the Sports Addon ($5/mo).",
 "pricing_url": "https://sports.bzzoiro.com/pricing/"}

List endpoints paginate with limit (default 50, max 200) and offset, and return {count, next, previous, results}.

Endpoints#

Endpoint Description
GET /hockey/api/v2/leagues/ List leagues
GET /hockey/api/v2/leagues/{id}/ League detail
GET /hockey/api/v2/teams/ List/search teams with ELO
GET /hockey/api/v2/teams/{id}/ Team detail
GET /hockey/api/v2/matches/ List matches
GET /hockey/api/v2/matches/live/ Matches in play right now
GET /hockey/api/v2/matches/{id}/ Match detail with period scores and odds
GET /hockey/api/v2/matches/{id}/odds/ Per-bookmaker odds, every market
GET /hockey/api/v2/matches/{id}/h2h/ Head-to-head record and season form
GET /hockey/api/v2/standings/ League table
GET /hockey/api/v2/predictions/ Model win probabilities
GET /hockey/api/v2/predictions/{id}/ One prediction

The machine-readable schema is public at /hockey/api/schema/, with an interactive UI at /hockey/api/docs/ and a themed explorer at /docs/explorer/hockey/.

Leagues#

Query parameters

Param Type Description
country string ISO country code
include_inactive bool Include inactive leagues
limit / offset int Pagination (default 50, max 200)
{"count": 14, "results": [
  {"id": 3, "name": "NHL", "country": "USA"}
]}

Teams#

Query parameters

Param Type Description
search string Name search
country string ISO country code
limit / offset int Pagination

Responses are cached ~5 minutes.

curl -H "Authorization: Token YOUR_API_KEY" \
  "https://sports.bzzoiro.com/hockey/api/v2/teams/?search=rangers"
{"count": 1, "results": [
  {"id": 118, "name": "New York Rangers", "short_name": "NYR",
   "country_code": "US", "elo_rating": 1671}
]}

Matches#

Query parameters

Param Type Description
league int Filter by league id
team int Matches involving this team id
status string scheduled, live, finished, awarded, postponed, cancelled
date_from / date_to date YYYY-MM-DD. Without either, the list returns the next 7 days, oldest first. Any of the filters above lifts that window and returns the full archive, newest first.
limit / offset int Pagination
curl -H "Authorization: Token YOUR_API_KEY" \
  "https://sports.bzzoiro.com/hockey/api/v2/matches/?team=118&status=finished&date_from=2026-04-01"
{"count": 22, "results": [
  {"id": 40312, "league": {"id": 3, "name": "NHL"},
   "home_team": {"id": 118, "name": "New York Rangers", "short_name": "NYR"},
   "away_team": {"id": 121, "name": "Boston Bruins", "short_name": "BOS"},
   "match_date": "2026-04-12T23:00:00+00:00", "status": "finished",
   "round_name": "Regular season", "home_score": 4, "away_score": 3,
   "is_overtime": true, "is_shootout": false, "winner_id": 118}
]}

GET /matches/live/ returns in-play matches only (cached 30 seconds). GET /matches/{id}/ adds period scores, the current period/minute for live games and decimal odds:

{"id": 40390, "status": "live",
 "home_score": 2, "away_score": 1,
 "periods_score": "1-0, 1-1", "current_period": 3, "current_minute": 7,
 "odds_home": 1.85, "odds_away": 1.95}

Note: is_overtime and is_shootout tell you how a finished game was decided — useful for regulation-time markets, where an overtime win counts differently.

Odds#

GET /hockey/api/v2/matches/{id}/odds/ returns every price we hold for one match: the match-winner market flattened per bookmaker, and below it every other market the books offered, grouped by kind, line and period.

curl -H "Authorization: Token YOUR_API_KEY" \
  "https://sports.bzzoiro.com/hockey/api/v2/matches/40390/odds/"
{"match_id": 40390, "match_date": "2026-04-12T23:00:00+00:00",
 "home_team_name": "New York Rangers", "away_team_name": "Boston Bruins",
 "bookmakers_count": 14, "source": "multi",
 "bookmakers": [
   {"bookmaker": "Bet365", "bookmaker_slug": "bet365",
    "odds_home": 2.54, "movement_home": "SHORTENING",
    "odds_draw": 4.21, "movement_draw": "STABLE",
    "odds_away": 2.40, "movement_away": "DRIFTING",
    "updated_at": "2026-04-12T21:31:48Z"}
 ],
 "markets": [
   {"market_kind": "1X2", "market_family": "1X2", "market_line": null,
    "market_period": "FT", "selections": ["HOME", "DRAW", "AWAY"],
    "bookmakers": [
      {"bookmaker": "Bet365", "bookmaker_slug": "bet365",
       "prices": {"HOME": {"price": 2.54, "movement": "SHORTENING"},
                  "DRAW": {"price": 4.21, "movement": "STABLE"},
                  "AWAY": {"price": 2.40, "movement": "DRIFTING"}},
       "updated_at": "2026-04-12T21:31:48Z"}
    ]}
 ]}
Field Meaning
source multi per-bookmaker prices, consensus the single stored price when the match is not yet linked to a priced feed event, none when nobody has priced it
bookmakers_count Bookmakers with at least one match-winner price
movement_* SHORTENING, DRIFTING or STABLE against the previous price we saw; null when there is no previous price
market_kind Market type: 1X2, DNB, AH, OU
market_family Distinguishes markets sharing a kind (total goals vs corners vs a single side's total)
market_line The handicap or total the market is quoted on; null for a plain winner market
market_period FT for the full match; period codes otherwise

Prices come from several sources and are pooled per match. A bookmaker appears once per market with its most recent price.

Standings#

GET /standings/?league={id} ranks a league by points, then points per game, then goal difference. Overtime results stay separate from regulation ones — in hockey they are different events, and a table that folds them together cannot be used for regulation-time markets.

curl -H "Authorization: Token YOUR_API_KEY" \
  "https://sports.bzzoiro.com/hockey/api/v2/standings/?league=3"
{"league": {"id": 3, "name": "NHL", "country": "USA"},
 "standings": [
   {"position": 1, "team": {"id": 118, "name": "New York Rangers", "short_name": "NYR"},
    "matches_played": 82, "wins": 45, "losses": 27,
    "overtime_wins": 6, "overtime_losses": 4,
    "points": 100, "points_per_game": 1.22,
    "goals_scored": 254, "goals_conceded": 219, "goal_difference": 35,
    "form_string": "WWLOTLW", "current_streak": 2,
    "calculated_at": "2026-08-19T05:38:00+00:00"}
]}

The table is built from the daily form calculation, so calculated_at tells you how fresh a row is. league is required.

Predictions#

Query parameters

Param Type Description
upcoming bool Only matches not yet started
limit / offset int Pagination
{"count": 7, "results": [
  {"id": 5107, "match_id": 40395,
   "home_win_prob": 0.55, "away_win_prob": 0.45,
   "predicted_winner_id": 118, "confidence": 0.55}
]}

Head-to-head and form#

GET /matches/{id}/h2h/ gives the record between the two teams and each side's season form, split home and away. Everything is expressed in this match's terms — home is the home team of this fixture, whichever way the record was originally stored.

curl -H "Authorization: Token YOUR_API_KEY" \
  "https://sports.bzzoiro.com/hockey/api/v2/matches/16264/h2h/"
{"match_id": 16264,
 "head_to_head": {"total_matches": 5, "home_wins": 4, "away_wins": 1,
   "home_goals": 15, "away_goals": 5, "avg_total_goals": 4.0,
   "recent_matches": [
     {"date": "2025-12-23T18:30:00+00:00", "winner": "home",
      "home_goals": 4, "away_goals": 1}]},
 "home_form": {"matches_played": 82, "wins": 45, "losses": 27,
   "overtime_wins": 6, "overtime_losses": 4, "points": 100,
   "points_per_game": 1.22, "form_string": "WWLOTLW", "current_streak": 2,
   "home": {"matches": 41, "wins": 26, "points_per_game": 1.44},
   "away": {"matches": 41, "wins": 19, "points_per_game": 1.0}},
 "away_form": {"...": "same shape"}}

head_to_head is null when the two teams have no recorded meeting, and a *_form is null for a team with no season record yet.

Coverage and off-season#

Hockey runs October to June. Between those months the match list is correctly empty, and an empty list on its own cannot tell you whether the season is over or the sync is broken.

GET /api/v2/coverage/?sport=hockey says so directly, without a token:

{"sport": "hockey", "status": "off_season", "events_next_7d": 0,
 "events_next_30d": 0, "priced_next_7d": 0, "live_now": 0,
 "next_event_at": null, "last_event_at": "2026-06-18T00:00:00+00:00"}

status is in_season (fixtures within seven days), between_events (nothing this week, something later), off_season (nothing ahead and the last event is more than a fortnight old) or no_fixtures. Drop the ?sport= to get every sport in one call — worth doing before a polling loop decides what to ask for.

View this page as Markdown · Found a mistake? Tell us