braket.gg
EN

Public REST API reference

Every Braket arena exposes a public, read-only REST API that mirrors the data on its public site. Use it to build Discord bots, stream overlays, standings widgets, or in-game "current tournaments" panels. This chapter documents the base URL, authentication, rate limits, and every endpoint with its parameters, an example request, an example response, and its status codes. It is accurate to src/web/routes/publicApi.ts.

Write access (submitting results) is not part of this API; it is the Steam-ticket-authenticated game-client protocol in The game-client result protocol (dual attestation). The same chapter also specifies the game-client recording and near-live segment endpoints (/api/v1/matches/:id/recording/init, /api/v1/recordings/…), which are Steam-ticket-authenticated as well and are not duplicated here. Future write scopes and webhooks are on the roadmap.

Base URL and tenancy

Each arena's API lives on its own host; the tenant is the subdomain:

https://<yourgame>.braket.gg/api/v1

or your arena's custom domain. Everything returned is scoped to that tenant; there is no cross-arena endpoint here.

All responses are application/json, versioned under /api/v1, with stable field names. All timestamps are ISO-8601 UTC strings. Players are identified by SteamID64. New fields may be added within v1, so be tolerant of extra fields.

Authentication and rate limits

Read endpoints are public and rate-limited to 60 requests per minute per IP. Sending a valid API key raises the limit to 600 per minute and prepares you for future private/write scopes:

Authorization: Bearer bk_<prefix>_<secret>

The key format is bk_<8 hex chars>_<secret>; the header must match ^Bearer (bk_[0-9a-f]{8}_[A-Za-z0-9_-]+)$. A key is bound to the tenant it was minted for and is ignored on any other host (the server checks the key's tenant equals the host's tenant). An unrecognized or wrong-tenant key is simply treated as anonymous (you get the 60/min limit), not rejected.

Mint keys in Admin, Site settings, API keys. The full key is shown once at creation and stored only as a SHA-256 hash. Revoke any time.

curl -H "Authorization: Bearer bk_1a2b3c4d_your_secret_here" \
  https://summergame.braket.gg/api/v1

Errors

Errors are an HTTP status plus a JSON body { "error": "<code>" }. Codes you can see: tournament_not_found, player_not_found, invalid_steam_id.

GET /api/v1

Meta / health. Confirms which tenant you are addressing and whether your key authenticated.

Parameters: none.

curl https://summergame.braket.gg/api/v1
{
  "api": "braket",
  "version": "1",
  "tenant": { "slug": "Summer Game", "name": "Summer Game" },
  "authenticated": true,
  "endpoints": [
    "GET /api/v1/tournaments",
    "GET /api/v1/tournaments/:slug",
    "GET /api/v1/ladder?season=",
    "GET /api/v1/players/:steamId"
  ]
}
Field Type Meaning
api string Always "braket"
version string API version, "1"
tenant object The arena (slug, name)
authenticated boolean Whether a valid API key for this tenant was presented
endpoints string[] The available endpoints

Status: 200.

GET /api/v1/tournaments

All non-draft tournaments (open, running, and past). Draft tournaments are never returned.

Parameters: none.

curl https://summergame.braket.gg/api/v1/tournaments
{
  "tournaments": [
    {
      "slug": "summer-cup",
      "name": "Summer Cup",
      "description": "Our flagship seasonal open.",
      "status": "registration_open",
      "format": "groups_playoffs",
      "registrationOpensAt": "2026-06-01T00:00:00Z",
      "registrationClosesAt": "2026-06-20T00:00:00Z",
      "startsAt": "2026-06-22T18:00:00Z",
      "maxPlayers": 32
    }
  ]
}

Each tournament summary:

Field Type Meaning
slug string Tournament slug (use in the :slug endpoint)
name string Display name
description string Description
status string One of registration_open, registration_closed, group_stage, playoffs, completed, cancelled (never draft)
format string groups_playoffs or playoffs_only
registrationOpensAt / registrationClosesAt string Registration window
startsAt string Group stage start (groupStageStartsAt)
maxPlayers number Registration cap

Status: 200.

GET /api/v1/tournaments/:slug

A full tournament: summary fields plus players, group standings, playoff bracket, and podium.

Path param Type Meaning
slug string The tournament slug
curl https://summergame.braket.gg/api/v1/tournaments/summer-cup
{
  "slug": "summer-cup",
  "name": "Summer Cup",
  "description": "Our flagship seasonal open.",
  "status": "playoffs",
  "format": "groups_playoffs",
  "registrationOpensAt": "2026-06-01T00:00:00Z",
  "registrationClosesAt": "2026-06-20T00:00:00Z",
  "startsAt": "2026-06-22T18:00:00Z",
  "maxPlayers": 32,
  "playerCount": 16,
  "players": [
    { "steamId": "76561197977425772", "personaName": "Kosmo", "avatarUrl": "https://…" }
  ],
  "groups": [
    {
      "name": "Group A",
      "standings": [
        { "rank": 1, "player": { "steamId": "…", "personaName": "…", "avatarUrl": "…" },
          "played": 3, "wins": 3, "losses": 0, "scoreDiff": 6, "points": 9 }
      ],
      "matches": [ { "id": 12, "stage": "group", "round": "Round 1", "bracketSlot": null,
                     "status": "confirmed", "opensAt": "…", "closesAt": "…",
                     "player1": { "…": "…" }, "player2": { "…": "…" }, "winner": { "…": "…" },
                     "score": { "p1": 2, "p2": 0 }, "resultSource": "player_report" } ]
    }
  ],
  "bracket": [
    { "label": "Quarterfinals", "side": "left",
      "matches": [ { "id": 42, "stage": "playoff", "round": "Quarterfinal 1",
                     "bracketSlot": "R1M1", "status": "confirmed", "opensAt": "…", "closesAt": "…",
                     "player1": {"…":"…"}, "player2": {"…":"…"}, "winner": {"…":"…"},
                     "score": { "p1": 2, "p2": 1 }, "resultSource": "game_client" } ] }
  ],
  "podium": [ { "rank": 1, "player": { "steamId": "…", "personaName": "…", "avatarUrl": "…" } } ]
}

Top-level shape:

Field Type Meaning
(summary fields) Same as the list endpoint
playerCount number Number of registrations
players array Registered players (steamId, personaName, avatarUrl)
groups array Group stage: each { name, standings[], matches[] }
bracket array Playoff columns: each { label, side, matches[] }
podium array Resolved podium: each { rank, player }

A standings row: { rank, player, played, wins, losses, scoreDiff, points }.

A bracket column: label is the round name, side is "left"/"right"/center placement in the bracket layout.

The match object (used in both groups[].matches and bracket[].matches):

Field Type Meaning
id number Match id
stage string group, playoff, small_final, or final
round string Human round label (roundLabel)
bracketSlot string or null Bracket position (R1M3, SF1, SMALL_FINAL, FINAL)
status string scheduled, live, awaiting_result, confirmed, disputed, forfeited
opensAt / closesAt string Match window
player1 / player2 object or null The two sides (null when undecided)
winner object or null The winner (null until resolved)
score object or null { p1, p2 } when both scores are set, else null
resultSource string or null player_report, admin, game_client, ai_video, or bye

A player object is always { steamId, personaName, avatarUrl } or null.

Status: 200; 404 { "error": "tournament_not_found" } if the slug is unknown or the tournament is a draft.

GET /api/v1/ladder

The season ladder for this arena.

Query param Type Default Meaning
season string latest season A season slug (for example 2026-q3) or all; anything else falls back to the latest season
curl "https://summergame.braket.gg/api/v1/ladder?season=2026-q3"
{
  "season": "2026-q3",
  "seasons": ["2026-q3"],
  "ladder": [
    { "rank": 1, "player": { "steamId": "…", "personaName": "…", "avatarUrl": "…" },
      "points": 320, "tournaments": 4 }
  ]
}
Field Type Meaning
season string The season actually returned
seasons string[] All available season slugs
ladder array Rows of { rank, player, points, tournaments } where tournaments is how many completed tournaments the player counted toward the season

Status: 200.

GET /api/v1/players/:steamId

A player's profile and stats for this arena.

Path param Type Meaning
steamId string A SteamID64; must match ^\d{17}$
curl https://summergame.braket.gg/api/v1/players/76561197977425772
{
  "player": { "steamId": "76561197977425772", "personaName": "Kosmo", "avatarUrl": "https://…" },
  "stats": {
    "matchesPlayed": 12,
    "wins": 8,
    "losses": 4,
    "winRate": 67,
    "trophies": [ { "rank": 1, "tournamentId": 3 } ]
  }
}
Field Type Meaning
player object { steamId, personaName, avatarUrl }
stats.matchesPlayed number Resolved matches (confirmed or forfeited) the player was in
stats.wins number Wins among those
stats.losses number matchesPlayed - wins
stats.winRate number Integer percentage, round(wins / matchesPlayed * 100), or 0 when none played
stats.trophies array Trophy grants: { rank, tournamentId }

Status: 200; 400 { "error": "invalid_steam_id" } if steamId is not 17 digits; 404 { "error": "player_not_found" } if no such player in this arena.

Quick client examples

// Latest ladder top 3
const r = await fetch('https://summergame.braket.gg/api/v1/ladder');
const { ladder } = await r.json();
console.log(ladder.slice(0, 3));
// A tournament's podium
const t = await fetch('https://summergame.braket.gg/api/v1/tournaments/summer-cup');
const { podium } = await t.json();