Skip to content

EsportsScore.gg API

The same data that powers this site — tournaments, matches, games and maps, standings, brackets, teams and players — as JSON, plus a realtime event stream. Version 1 is stable: fields are only ever added, so ignore fields you do not recognise. OpenAPI 3.1 document.

Quick start

Base URL: https://esportsscore.citranagakencana.com/api/v1

curl "https://esportsscore.citranagakencana.com/api/v1/matches?status=live" \
  -H "Authorization: Bearer $API_KEY"

curl "https://esportsscore.citranagakencana.com/api/v1/tournaments/<tournament-slug>/standings"

Every entity is addressable by id or by its slug (/teams/<team-slug>). Slugs may change — old ones keep resolving — while ids never do, so store ids.

Authentication

Send an API key as Authorization: Bearer <key> or X-API-Key: <key>. Keys are server-side secrets: never ship them in browser or mobile code. Ask the operator of this site for a key; each consumer gets its own client with its own limits and usage reporting.

Requests without a key are accepted with a lower, per-address limit. The realtime stream, live snapshots and search always accept keyless requests (this website uses them from the browser).

ScopeGrants
read:publicGames, tournaments, matches, standings, brackets, teams, players, search
read:liveRealtime stream, live snapshots and the uncached match endpoint

Rate limits

Limits are per minute with bursts allowed: a client may spend its whole allowance at once, which then refills continuously. Keyless requests: 120 per minute per address. Keys: 600 per minute per client unless agreed otherwise. Up to 50 realtime streams may be open per address.

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds until the allowance is full). Over the limit you get 429 with Retry-After in seconds.

Conventions

  • Single objects come as { data }, lists as { data: [...], meta }.
  • Lists are paginated with cursors: pass meta.nextCursor back as cursor until it is null. Cursors are opaque; limit is 1–100 (default 25).
  • Timestamps are ISO-8601 in UTC; calendar dates are YYYY-MM-DD; tournaments state their timezone.
  • Objects that change carry a version; within one object, a higher version is always newer.
  • Terminology is per game (terminology on games and matches): a MOBA plays games, a tactical shooter maps, a battle royale a matchday of games.
  • Draft and private tournaments never appear. Unlisted tournaments are reachable by id or slug but not listed.

Caching

Responses carry a weak ETag; send it back as If-None-Match to get 304 Not Modified without a body. Lists may be served from shared caches for up to 30 seconds and details for 15 seconds; live endpoints and the stream are never cached. For anything time-critical, use the stream.

Errors

Errors use one envelope; branch on code, not on messages, and quote requestId (also in the X-Request-Id header) when asking for help.

{ "error": { "code": "NOT_FOUND", "message": "Match not found", "requestId": "…" } }
400 VALIDATION_FAILED401 UNAUTHENTICATED403 FORBIDDEN404 NOT_FOUND429 RATE_LIMITED503 BUSY_RETRY503 UNAVAILABLE500 INTERNAL

Realtime

GET /api/v1/stream?channels=… is a Server-Sent Events stream. Channels: live (every live match, summaries at most once per second per match), game:<id>, tournament:<id> and contest:<matchId> (every change, immediately).

  1. Connect. After you are subscribed the server sends ready.
  2. Then fetch the current state from /api/v1/live/snapshots?keys=… (for example match:<id>,standings:<stageId>). Events that arrive meanwhile are not lost: apply any event whose aggregate.version is higher than what you hold.
  3. Events (match.updated, score.updated, segment.completed, standing.updated, bracket.updated, …) carry the full object in payload. Ignore duplicates by event id.
  4. ping arrives every 20 s; reconnect if nothing arrives for 45 s. A sync frame means the server may have missed events: fetch snapshots again. reconnect asks you to reconnect (deploys).
const es = new EventSource("https://esportsscore.citranagakencana.com/api/v1/stream?channels=contest:" + matchId);
es.addEventListener("ready", async () => {
  const res = await fetch("https://esportsscore.citranagakencana.com/api/v1/live/snapshots?keys=match:" + matchId);
  apply((await res.json()).data["match:" + matchId]);
});
es.addEventListener("match.updated", (e) => {
  const event = JSON.parse(e.data);
  if (event.aggregate.version > current.match.version) apply(event.payload);
});

Endpoint reference

Games

  • GET/api/v1/gamesList games

    Every active game with its terminology (what a match, game/map and score are called).

  • GET/api/v1/games/{game}Get a game
    game*
    Game id or slug

Tournaments

  • GET/api/v1/tournamentsList tournaments

    Published, listed tournaments, newest start date first.

    game
    Only tournaments of this game (id or slug)
    status
    Only tournaments in this status (upcoming, ongoing, completed, cancelled, postponed)
    limit
    Page size, 1–100
    cursor
    Opaque cursor: meta.nextCursor of the previous page
  • GET/api/v1/tournaments/{tournament}Get a tournament

    Tournament details with its stages (format, groups, rounds). Unlisted tournaments are reachable here.

    tournament*
    Tournament id or slug
  • GET/api/v1/tournaments/{tournament}/matchesList a tournament's matches
    tournament*
    Tournament id or slug
    stage
    Only matches of this stage (id)
    status
    live (live, paused; by start), upcoming (scheduled, check-in, delayed, postponed; by start time), completed (completed, forfeit; newest first) or all (by start time)
    limit
    Page size, 1–100
    cursor
    Opaque cursor: meta.nextCursor of the previous page
  • GET/api/v1/tournaments/{tournament}/standingsGet standings

    Standings of every league, round-robin, Swiss and battle-royale stage. Columns are format-specific (columns describes them); view=live includes games still in progress (provisional).

    tournament*
    Tournament id or slug
    stage
    Only this stage (id)
    view
    official (completed games) or live
  • GET/api/v1/tournaments/{tournament}/bracketGet brackets

    Elimination stages as rounds of matches, with placeholders ("Winner of …") for undecided slots.

    tournament*
    Tournament id or slug
    stage
    Only this stage (id)
  • GET/api/v1/tournaments/{tournament}/participantsList participants

    Registered participants with seeds and tournament rosters.

    tournament*
    Tournament id or slug

Matches

  • GET/api/v1/matchesList matches

    Matches of listed tournaments across games. from/to filter on the scheduled (or actual) start.

    status
    live (live, paused; by start), upcoming (scheduled, check-in, delayed, postponed; by start time), completed (completed, forfeit; newest first) or all (by start time)
    game
    Only matches of this game (id or slug)
    tournament
    Only matches of this tournament (id or slug)
    team
    Only matches of this team (id or slug)
    from
    Start at or after (ISO-8601 with offset)
    to
    Start before (ISO-8601 with offset)
    limit
    Page size, 1–100
    cursor
    Opaque cursor: meta.nextCursor of the previous page
  • GET/api/v1/matches/{match}Get a match

    Full match state: participants, series score and every game/map with its results.

    match*
    Match id or slug
  • GET/api/v1/matches/{match}/liveGet a match (uncached)read:live

    Same as Get a match but never cached anywhere; for polling clients. Prefer the stream.

    match*
    Match id or slug

Teams

  • GET/api/v1/teamsList teams
    game
    Only teams of this game (id or slug)
    q
    Name or short name contains
    limit
    Page size, 1–100
    cursor
    Opaque cursor: meta.nextCursor of the previous page
  • GET/api/v1/teams/{team}Get a team

    Team with its current roster, former members and tournaments.

    team*
    Team id or slug
  • GET/api/v1/teams/{team}/matchesList a team's matches
    team*
    Team id or slug
    status
    live (live, paused; by start), upcoming (scheduled, check-in, delayed, postponed; by start time), completed (completed, forfeit; newest first) or all (by start time)
    limit
    Page size, 1–100
    cursor
    Opaque cursor: meta.nextCursor of the previous page

Players

  • GET/api/v1/playersList players
    game
    Only players of this game (id or slug)
    team
    Only current members of this team (id or slug)
    q
    Nickname or real name contains
    limit
    Page size, 1–100
    cursor
    Opaque cursor: meta.nextCursor of the previous page
  • GET/api/v1/players/{player}Get a player

    Player with team history and tournaments.

    player*
    Player id or slug

Search

Realtime

  • GET/api/v1/live/snapshotsGet live snapshotsread:live

    Consistent current state for realtime clients, read in one transaction. Keys: match:<id>, standings:<stageId>, bracket:<stageId>, tournament:<id>, live or live:<gameId>. Fetch after every stream ready or sync frame. Values are null when not visible.

    keys*
    Comma-separated snapshot keys (max 50)
  • GET/api/v1/streamRealtime event stream (SSE)read:live

    Server-Sent Events. Channels: live, game:<id>, tournament:<id>, contest:<id>. Frames: ready (after subscription), event frames (match.updated, score.updated, standing.updated, …) with full DTO payloads and a monotonic aggregate.version, ping every 20 s, sync (resubscribe: fetch snapshots) and reconnect.

    channels*
    Comma-separated channels

Meta

  • GET/api/v1/openapi.jsonOpenAPI document

    This API described as OpenAPI 3.1, generated from the same contracts the server validates.

Building something on top of this data? The operators behind EsportsScore.gg issue keys with higher limits on request.