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).
| Scope | Grants |
|---|---|
| read:public | Games, tournaments, matches, standings, brackets, teams, players, search |
| read:live | Realtime 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.nextCursorback ascursoruntil it isnull. Cursors are opaque;limitis 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 (
terminologyon 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": "…" } }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).
- Connect. After you are subscribed the server sends
ready. - Then fetch the current state from
/api/v1/live/snapshots?keys=…(for examplematch:<id>,standings:<stageId>). Events that arrive meanwhile are not lost: apply any event whoseaggregate.versionis higher than what you hold. - Events (
match.updated,score.updated,segment.completed,standing.updated,bracket.updated, …) carry the full object inpayload. Ignore duplicates by eventid. pingarrives every 20 s; reconnect if nothing arrives for 45 s. Asyncframe means the server may have missed events: fetch snapshots again.reconnectasks 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 gamesEvery 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 tournamentsPublished, 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.nextCursorof the previous page
- GET
/api/v1/tournaments/{tournament}Get a tournamentTournament 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) orall(by start time)- limit
- Page size, 1–100
- cursor
- Opaque cursor:
meta.nextCursorof the previous page
- GET
/api/v1/tournaments/{tournament}/standingsGet standingsStandings of every league, round-robin, Swiss and battle-royale stage. Columns are format-specific (
columnsdescribes them);view=liveincludes games still in progress (provisional).- tournament*
- Tournament id or slug
- stage
- Only this stage (id)
- view
official(completed games) orlive
- GET
/api/v1/tournaments/{tournament}/bracketGet bracketsElimination 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 participantsRegistered participants with seeds and tournament rosters.
- tournament*
- Tournament id or slug
Matches
- GET
/api/v1/matchesList matchesMatches of listed tournaments across games.
from/tofilter 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) orall(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.nextCursorof the previous page
- GET
/api/v1/matches/{match}Get a matchFull 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:liveSame 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.nextCursorof the previous page
- GET
/api/v1/teams/{team}Get a teamTeam 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) orall(by start time)- limit
- Page size, 1–100
- cursor
- Opaque cursor:
meta.nextCursorof 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.nextCursorof the previous page
- GET
/api/v1/players/{player}Get a playerPlayer with team history and tournaments.
- player*
- Player id or slug
Search
- GET
/api/v1/searchSearchRanked search over teams, players, tournaments, matches, games and organizations.
- q*
- Search text (typos tolerated)
- types
- Comma-separated result types to include
- game
- Only results of this game (id or slug)
- limit
- Results, 1–50
Realtime
- GET
/api/v1/live/snapshotsGet live snapshotsread:liveConsistent current state for realtime clients, read in one transaction. Keys:
match:<id>,standings:<stageId>,bracket:<stageId>,tournament:<id>,liveorlive:<gameId>. Fetch after every streamreadyorsyncframe. Values are null when not visible.- keys*
- Comma-separated snapshot keys (max 50)
- GET
/api/v1/streamRealtime event stream (SSE)read:liveServer-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 monotonicaggregate.version,pingevery 20 s,sync(resubscribe: fetch snapshots) andreconnect.- channels*
- Comma-separated channels
Meta
- GET
/api/v1/openapi.jsonOpenAPI documentThis 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.