Pro vývojáře

HockeyBook
API dokumentace

Read-only REST API pro získávání dat o ligách, sezónách, zápasech, statistikách hráčů, tabulkách a playoff. Vrací pouze data těch lig/organizací, kde je vlastník API klíče admin.

Verze 1.0REST / JSONRead-only

Rychlý start

Každý požadavek musí obsahovat hlavičku x-api-key. Klíč získáte ze svého profilu v HockeyBook aplikaci (pole User.apiKey).

Base URL (prod)https://api.hockeybook.online
Base URL (dev)http://localhost:7500
Prefix/api
Formátapplication/json
Interaktivní docs/docs (Swagger UI)
OpenAPI spec/openapi.yaml
curl -H "x-api-key: YOUR_KEY" \
  https://api.hockeybook.online/api/leagues

Autentizace

API používá API klíč v HTTP hlavičce x-api-key. Klíč je svázán s konkrétním uživatelem. Všechny endpointy automaticky filtrují data pouze pro ligy, ve kterých je tento uživatel uveden jako administrátor.

  • Chybějící hlavička → 401 Unauthorized
  • Neplatný klíč nebo nedostatečná oprávnění → 403 Forbidden
  • Klíč nikdy neposílejte z frontendu prohlížeče; používejte ho výhradně server-to-server.

Limity, cache & CORS

Rate limit

Každý API klíč má limit 120 požadavků za minutu na všechny /api/* endpointy (klouzavé okno 60 s). Pokud limit překročíte, API vrátí 429 Too Many Requests s hlavičkou Retry-After. Pro vyšší limity nás kontaktujte.

Cache

Všechny GET endpointy vracejí Cache-Control: private, max-age=…, stale-while-revalidate=… a slabý ETag. Hlavička Vary: x-api-key, Accept-Encoding zajišťuje, že per-user data nemohou být sdílena přes proxy/CDN.

  • Eventy, rozpis, kalendář, pinned: max-age=30, SWR 60 s
  • Tabulka, statistiky, playoff, ligy: max-age=120, SWR 5 min
  • Články, stránky, kempy: max-age=600, SWR 30 min

Pošlete If-None-Match s předchozím ETag a server odpoví 304 Not Modified bez payloadu, pokud se data nezměnila.

Velikost požadavku

Maximální velikost JSON těla je 100 kB (vrací 413 Payload Too Large).

CORS

API je primárně určené pro server-to-server volání. Volání z prohlížeče je povoleno pouze z explicitně whitelistovaných origins. Klíč nikdy nevystavujte v klientském JavaScriptu — proxy ho přes vlastní backend.

Chybové odpovědi

Všechny chyby vrací JSON ve tvaru:

{ "error": "popis chyby", "code": "INVALID_MONTH" }

Pole code je stabilní strojově čitelný identifikátor (může chybět u 500).

StatusCodeVýznam
400INVALID_IDNeplatný ObjectId v URL
400INVALID_MONTHFormát month není YYYY-MM
400TOO_MANY_ORGSV orgs je víc než 30 položek
400MISSING_PARAMSChybí povinný query parametr
400INVALID_PHASENeznámá hodnota phase
400INVALID_SLUGSlug neodpovídá ^[a-z0-9][a-z0-9_-]{0,199}$
400VALIDATIONValidační chyba payloadu
401MISSING_API_KEYChybí hlavička x-api-key
403INVALID_API_KEYNeplatný klíč
403NO_ACCESSK této lize/organizaci nemáte přístup
404NO_SEASONLiga nemá aktivní sezónu
404NOT_FOUNDZáznam (článek, stránka, sezóna) neexistuje
429Překročen rate limit
500Interní chyba serveru

Přehled endpointů

MetodaCestaPopis
GET/healthHealth check (bez auth)
GET/api/leaguesSeznam lig uživatele
GET/api/leagues/:leagueId/eventsEventy ligy (stránkované)
GET/api/leagues/:leagueId/schedulePoslední + nadcházející zápasy
GET/api/orgs/:orgId/campsKempy organizace
GET/api/orgs/:orgId/pinned-eventsPřipnuté nadcházející eventy
GET/api/calendarEventy v měsíci napříč orgs
GET/api/leagues/:leagueId/standingsTabulka týmů
GET/api/leagues/:leagueId/stats/skatersStatistiky hráčů
GET/api/leagues/:leagueId/stats/goaliesStatistiky brankářů
GET/api/leagues/:leagueId/playoffPlayoff (skupiny, bracket)
GET/api/articlesPublikované články
GET/api/articles/:slugDetail článku
GET/api/pagesPublikované stránky
GET/api/pages/:slugDetail stránky

GET /api/leagues

Vrací pole lig, kde je uživatel admin.

Odpověď

[
  {
    "_id": "65f0a1b2c3d4e5f60718293a",
    "title": "Hobby Liga Norte",
    "urlTitle": "hobby-liga-norte",
    "type": "league",
    "status": "active",
    "imageUrl": "/uploads/leagues/abc.png",
    "primaryColor": "#00C2FF",
    "secondaryColor": "#0B1525",
    "activeSeason": {
      "_id": "65f0...",
      "year": "2025/2026",
      "active": true,
      "dateFrom": "2025-09-01T00:00:00.000Z",
      "dateTo": "2026-04-30T00:00:00.000Z"
    },
    "location": "Praha",
    "isPublic": true
  }
]

GET /api/leagues/:leagueId/events

Eventy dané ligy. Stránkováno.

Query parametry

ParametrTypVýchozíPopis
seasonIdstringactiveSeasonID sezony
limitint50 (max 200)Počet záznamů
pageint1Stránka

Odpověď

{
  "events": [
    {
      "_id": "...",
      "title": "HC Praha vs HC Brno",
      "type": "match",
      "dateFromRaw": "2026-01-15T18:00:00.000Z",
      "dateFrom": "15.1.2026",
      "timeFrom": "18:00",
      "place": "Zimní stadion Praha",
      "homeTeamId": { "_id": "...", "name": "HC Praha", "imageUrl": "...", "primaryTeamColor": "#fff", "secondaryTeamColor": "#000" },
      "awayTeamId": { "_id": "...", "name": "HC Brno", "imageUrl": "..." },
      "gameStatus": { "matchCompleted": true, "home": { "goals": 3 }, "away": { "goals": 2 }, "overtime": true },
      "group": "A"
    }
  ],
  "total": 42, "page": 1, "limit": 50
}

GET /api/leagues/:leagueId/schedule

Posledních past dohraných + nejbližších upcoming zápasů.

Query parametry

ParametrTypVýchozí
seasonIdstringactiveSeason
pastint5 (max 50)
upcomingint5 (max 50)

Odpověď

{
  "schedule": [
    {
      "_id": "...",
      "dateFromRaw": "2026-01-15T18:00:00.000Z",
      "dateFrom": "15.1.2026",
      "timeFrom": "18:00",
      "place": "Zimní stadion Praha",
      "homeTeam": { "_id": "...", "name": "HC Praha", "imageUrl": "..." },
      "awayTeam": { "_id": "...", "name": "HC Brno", "imageUrl": "..." },
      "score": "3:2pp",
      "completed": true,
      "group": "A"
    }
  ]
}

Skóre má tvar H:A; přípona pp = prodloužení, sn = nájezdy.

GET /api/orgs/:orgId/camps

Kempy organizace (eventy s ikonou camp nebo typem camp). Vrací budoucí + posledních ~2 měsíce.

Odpověď

{
  "camps": [
    {
      "_id": "...",
      "title": "Letní kemp 2026",
      "dateFromRaw": "2026-07-01T08:00:00.000Z",
      "dateToRaw":   "2026-07-07T16:00:00.000Z",
      "dateFrom": "1.7.2026",
      "dateTo":   "7.7.2026",
      "timeFrom": "08:00",
      "timeTo":   "16:00",
      "place": "ZS Slavia",
      "description": "Celodenní kemp pro děti 8–14 let.",
      "price": 4500
    }
  ],
  "org": { "_id": "...", "title": "HC Praha" }
}

GET /api/orgs/:orgId/pinned-events

Připnuté budoucí eventy (max 10). Pokud má organizace nastavený displayTeam, zahrnuje i eventy, kde tento tým hraje.

Odpověď

{
  "events": [
    {
      "_id": "...",
      "title": "HC Praha vs HC Brno",
      "type": "match",
      "icon": "mdi-hockey-puck",
      "dateFromRaw": "2026-02-10T19:00:00.000Z",
      "dateFrom": "10.2.2026",
      "timeFrom": "19:00",
      "place": "ZS Slavia",
      "homeTeam": { "_id": "...", "name": "HC Praha", "imageUrl": "..." },
      "awayTeam": { "_id": "...", "name": "HC Brno", "imageUrl": "..." },
      "players":  { "capacity": 20, "signedUp": 14, "substitutes": 2 },
      "goalies":  { "capacity": 2,  "signedUp": 2,  "substitutes": 0 },
      "isLeagueMatch": true
    }
  ]
}

GET /api/calendar

Eventy daného měsíce napříč více organizacemi.

Query parametry (povinné)

ParametrTypPříklad
monthstring2026-07 (formát YYYY-MM)
orgsstringid1,id2,id3 (CSV ObjectId, max 30)

Odpověď

{
  "events": [
    {
      "_id": "...",
      "title": "HC Praha vs HC Brno",
      "type": "match",
      "icon": "default",
      "dateFromRaw": "2026-07-12T18:00:00.000Z",
      "dateFrom": "12.7.2026",
      "timeFrom": "18:00",
      "place": "ZS Slavia",
      "league": "65f0...",
      "homeTeam": { "_id": "...", "name": "HC Praha", "shortName": "PRA", "imageUrl": "..." },
      "awayTeam": { "_id": "...", "name": "HC Brno",  "shortName": "BRN", "imageUrl": "..." },
      "homeAttendants": { "players": 12, "goalies": 1 },
      "awayAttendants": { "players": 11, "goalies": 1 },
      "score": "3:2",
      "completed": true,
      "players": { "capacity": 40, "signedUp": 23, "substitutes": 4 },
      "goalies": { "capacity": 4,  "signedUp": 2,  "substitutes": 0 },
      "isLeagueMatch": true
    }
  ],
  "orgs": [
    { "_id": "...", "title": "HC Praha", "color": "#00C2FF", "displayTeam": "..." }
  ]
}

GET /api/leagues/:leagueId/standings

Tabulka týmů pro sezónu (s vyřešením shody bodů přes nastavené tiebreakery).

Query parametry

seasonIdvolitelné, výchozí activeSeason

Odpověď

{
  "league":  { "_id": "...", "title": "Hobby Liga", "urlTitle": "hobby-liga" },
  "season":  { "_id": "...", "year": "2025/2026", "groupsCount": 2 },
  "standings": [
    {
      "team": { "_id": "...", "name": "HC Praha", "imageUrl": "...", "primaryTeamColor": "#fff", "secondaryTeamColor": "#000" },
      "gamesPlayed": 24,
      "wins": 18, "overtimeWins": 1, "overtimeLosses": 2,
      "losses": 3, "draws": 0,
      "goalsFor": 96, "goalsAgainst": 42, "goalDifference": 54,
      "points": 58
    }
  ]
}

GET /api/leagues/:leagueId/stats/skaters

Statistiky bruslařů. Agregováno přes všechny fáze (regular/playoff/groups/consolation) do jednoho řádku per hráč. Filtrováno na minimální počet odehraných zápasů.

Query parametry

ParametrHodnotyVýchozí
seasonIdstringactiveSeason
phaseall, regular, playoff, groups, consolationall
sortpoints, goals, assists, plusMinus, …points
orderasc / descdesc
limitint (max 200)50
pageint1

Odpověď

{
  "skaters": [
    {
      "player": { "_id": "...", "firstName": "Jan", "lastName": "Novák", "nickName": "Honza", "avatar": "..." },
      "team":   { "_id": "...", "name": "HC Praha", "imageUrl": "..." },
      "gamesPlayed": 22,
      "goals": 18, "assists": 24, "points": 42,
      "penaltyMinutes": 14,
      "plusMinus": 12,
      "powerPlayGoals": 5,
      "shotsOnGoal": 72,
      "shootingPct": 25.0,
      "pointsPerGame": 1.91
    }
  ],
  "total": 87, "page": 1, "limit": 50
}

GET /api/leagues/:leagueId/stats/goalies

Statistiky brankářů. Stejná logika filtrování jako u skaterů.

Query parametry

ParametrHodnotyVýchozí
seasonIdstringactiveSeason
phaseall, regular, playoff, groups, consolationall
sortsavePct, goalsAgainstAverage, wins, …savePct
orderasc / descdesc
limitint (max 200)50
pageint1

Odpověď

{
  "goalies": [
    {
      "player": { "_id": "...", "firstName": "Petr", "lastName": "Svoboda", "avatar": "..." },
      "team":   { "_id": "...", "name": "HC Praha", "imageUrl": "..." },
      "gamesPlayed": 18,
      "wins": 14, "losses": 3, "ties": 1,
      "shutouts": 2,
      "goalsAgainst": 32,
      "goalsAgainstAverage": 1.78,
      "saves": 412,
      "shotsAgainst": 444,
      "savePct": 92.79,
      "penaltyMinutes": 0
    }
  ],
  "total": 12, "page": 1, "limit": 50
}

GET /api/leagues/:leagueId/playoff

Kompletní playoff payload: skupiny s tabulkami, útěšenecká skupina, konfigurace bracketu (větve, postupová pravidla, zápasy o umístění), série vítězství a všechny playoff eventy pro vykreslení pavouka.

Odpověď (zkráceno)

{
  "league":  { "_id": "...", "title": "Hobby Liga", "urlTitle": "hobby-liga" },
  "season":  { "_id": "...", "year": "2025/2026" },
  "playoffGroups": [
    {
      "label": "A",
      "standings": [
        {
          "team": { "_id": "...", "name": "HC Praha", "imageUrl": "..." },
          "placeholder": false,
          "seed": 0,
          "label": "ZČ-1",
          "gamesPlayed": 6, "wins": 5, "overtimeWins": 0, "overtimeLosses": 1,
          "losses": 0, "goalsFor": 24, "goalsAgainst": 9, "goalDifference": 15,
          "points": 16, "bonusPoints": 5
        }
      ]
    }
  ],
  "consolationGroup": [ /* stejné schéma jako standings entry */ ],
  "playoffConfig": {
    "scenarioType": "groups+bracket",
    "groupCount": 2,
    "groupFormat": "round-robin",
    "consolationGroupEnabled": true,
    "consolationGroupName": "Utěšenecká skupina",
    "branches": [
      {
        "id": "main",
        "name": "Hlavní pavouk",
        "color": "#00C2FF",
        "teams_from": "groups",
        "team_count": 8,
        "preliminary_rounds": 0,
        "preliminary_series": 0,
        "rounds": [ { "name": "Čtvrtfinále", "series": 4 }, { "name": "Semifinále", "series": 2 } ]
      }
    ],
    "advancementRules": [
      { "id": "r1", "description": "Top 4 z A+B postupují", "source": {...}, "target": {...}, "pairing": null }
    ],
    "placementMatches": [
      { "place": "3", "name": "O 3. místo", "teams_from": "semifinále", "series": 1 }
    ]
  },
  "groupResults": [
    { "groupIndex": 0, "groupLetter": "A", "position": 1,
      "teamId": "...", "teamName": "HC Praha", "teamLogo": "...",
      "points": 21, "placeholder": false, "label": "ZČ-1" }
  ],
  "seriesWins": {
    "65f01_65f02": { "65f01": 2, "65f02": 1 }
  },
  "bracketEvents": [
    {
      "_id": "...",
      "playoffMeta": { "branchId": "main", "roundIndex": 0, "seriesIndex": 0, "gameIndex": 0, "roundType": "quarterfinal" },
      "meta": { "homeTeam": { "_id": "...", "name": "HC Praha", "imageUrl": "..." },
                "awayTeam": { "_id": "...", "name": "HC Brno",  "imageUrl": "..." } },
      "gameStatus": { "matchCompleted": true, "homeGoals": 3, "awayGoals": 2 }
    }
  ]
}

Klíč v seriesWins je teamIdA_teamIdB (seřazené ID), hodnota je mapa teamId → počet vyhraných zápasů v sérii.

GET /api/articles

Publikované články ze všech lig uživatele (kromě typu page).

Query parametry

limitint (max 20), výchozí 9
pageint, výchozí 1

Odpověď

{
  "articles": [
    {
      "_id": "...",
      "urlTitle": "vitezstvi-hc-praha",
      "title": "Vítězství HC Praha 5:1",
      "type": "news",
      "publishDate": "2026-01-20T10:00:00.000Z",
      "intro": "HC Praha porazila…",
      "imageUrl": "/uploads/articles/abc.jpg"
    }
  ],
  "total": 17, "page": 1, "pages": 2
}

GET /api/articles/:slug

Detail článku podle urlTitle.

{
  "_id": "...",
  "urlTitle": "vitezstvi-hc-praha",
  "title": "Vítězství HC Praha 5:1",
  "type": "news",
  "publishDate": "2026-01-20T10:00:00.000Z",
  "intro": "HC Praha porazila…",
  "content": "<p>Plný HTML obsah článku…</p>",
  "imageUrl": "/uploads/articles/abc.jpg"
}

GET /api/pages

Publikované stránky (články typu page).

{
  "pages": [
    {
      "_id": "...",
      "urlTitle": "o-nas",
      "title": "O nás",
      "publishDate": "2025-12-01T00:00:00.000Z",
      "intro": "Krátký úvod…",
      "imageUrl": "..."
    }
  ]
}

GET /api/pages/:slug

Detail stránky podle urlTitle.

{
  "_id": "...",
  "urlTitle": "o-nas",
  "title": "O nás",
  "type": "page",
  "publishDate": "2025-12-01T00:00:00.000Z",
  "intro": "Krátký úvod…",
  "content": "<p>Plný HTML obsah stránky…</p>",
  "imageUrl": "..."
}
📧

Potřebujete přístup k API, vyšší limity nebo máte dotaz? Napište nám na info@hockeybook.online.