REDSEC

The API

Everything on this site is readable as JSON. No key, no sign-up, no quota to apply for. The record here was assembled by hand, and it is more useful to other people than it is sitting behind a form.

Identify yourself

Send an X-Client header on every request, naming your client and how to reach you:

X-Client: my-overlay (https://example.com)
X-Client: season-report ([email protected])

There is no key, so this is the only way to tell who is reading the API — which is what makes it possible to warn you before something changes rather than breaking your client without notice. A request without it is answered normally today and carries an X-Api-Notice header asking for one. Requests that never identify themselves may be refused later.

Endpoints

GET /api/v1/events
GET /api/v1/events/{slug}
GET /api/v1/events/{slug}/stages
GET /api/v1/events/{slug}/standings
GET /api/v1/events/{slug}/awards
GET /api/v1/match-days/{id}
GET /api/v1/match-days/{id}/standings
GET /api/v1/match-days/{id}/streams
GET /api/v1/match-days/{id}/live        server-sent events
GET /api/v1/lobbies/{id}
GET /api/v1/matches/{id}
GET /api/v1/teams
GET /api/v1/teams/{slug}
GET /api/v1/teams/{slug}/roster?at={date}
GET /api/v1/teams/{slug}/results
GET /api/v1/teams/{slug}/stats
GET /api/v1/teams/{slug}/vs/{other}     co-lobby head-to-head
GET /api/v1/teams/{slug}/earnings      per cup, from published splits
GET /api/v1/players/{slug}
GET /api/v1/players/{slug}/stats
GET /api/v1/players/{slug}/teams
GET /api/v1/players/{slug}/awards
GET /api/v1/players/{slug}/vs/{other}
GET /api/v1/players/{slug}/earnings
GET /api/v1/rankings?region={region}
GET /api/v1/rankings/global?limit={n}
GET /api/v1/rankings/history
GET /api/v1/stats/leaderboard            filters below
GET /api/v1/news
GET /api/v1/news/{slug}
GET /api/v1/search?q={query}
GET /api/v1/transfers?region={region}&cursor={cursor}

The /rankings endpoints serve the Redline Ranking. rating is the share it is computed as and points the published figure; the methodology says how both are made.

RSR

RSR, the REDSEC Rating, is the site's headline player number: 1.00 is the average player-game. The methodology has the formula and the weights. It appears in the API as additional fields; no existing field was removed or changed.

  • /players/{slug}/stats adds rsr (career), rsr_games, carry and rsr_version.
  • /matches/{id} adds rsr to each player line.
  • /stats/leaderboard lists rsr among its player columns and accepts metric=rsr. A request with no sort still orders by kills per game, as it always has; the page sorts by RSR.
  • /events/{slug}/awards and /players/{slug}/awards list event MVPs and EVPs, with the rsr_version that named them.

RSR values are unrounded; the site prints them to two decimals. A player with no rated line has null, not 0. Impact, the rating RSR replaces, was only ever shown on pages and never returned by the API, so there is no impact field to keep or remove.

The rules

  • Slugs, never ids. Internal ids do not appear in responses, so nothing you store against a slug breaks when the database is reorganised.
  • Regions are upper case — AMERICAS, EMEA, APAC — though the parameter accepts any casing.
  • Null means unknown, zero means zero. A missing stat is null. It is never rounded down to nothing, because an average over invented zeroes is a wrong number that looks right.
  • ETags on every read. Send If-None-Match and get a 304 when nothing has changed.
  • Errors are RFC 9457 problem details, not bare strings.
  • Rate limited per IP. Over the limit returns 429. The live stream is one long-lived connection, not a poll — use it rather than requesting standings in a loop.

Leaderboards

/api/v1/stats/leaderboard is the table on /leaderboards, with the same filters. The page's query string works here unchanged, so the quickest way to build a request is to set the table up on the page and copy its link. One difference: the minimum defaults to 10 games here and 4 on the page, so pass min_matches to match the page exactly.

view=squads            players by default
span=30d|60d|90d|2026  a rolling window ending today, or a calendar year
from=2026-09-01        a custom range; either end may be left open
to=2026-09-30
tier=2                 event tier
region=EMEA            AMERICAS, EMEA or APAC
event={slug}           one event
min_matches=10         minimum games to be listed (alias: min); default 10
sort={column}          any column key (alias: metric); default kills_per_match
                       for players, avg_placement for squads
dir=asc|desc           default: the column's better end first
limit=50               rows returned, up to 500

Filters choose games, not players: a squad's EMEA line is the games it played in EMEA events. The response carries the scope it was computed over, total (rows that clear the minimum), played (everyone with a game in scope), the columns with their definitions, and data. Each row holds every column, keyed as in columns.

Rates are computed over only the games that sourced the stat: damage per game is damage divided by the games whose line carried damage, not by every game played. A squad's summed damage counts only games where every player's line is on record. Where no game in scope sourced a stat, the value is null.

The live stream

/api/v1/match-days/{id}/live is server-sent events. The first frame is the current table, so a fresh connection has something to show immediately, and each later frame is the whole table again rather than a diff. If the stream is at capacity it answers 503 with a poll_interval_secs telling you how often to fall back to polling /standings instead.

Transfers

/api/v1/transfers is every roster move, newest first, fifty to a page; pass the cursor from one page to get the next. Nobody publishes transfers for this circuit, so each move is read off the record, and each says what it was read off: source is result (a sourced game showed the player on the new squad), signup (the organizer's entry list shows them captaining it) or admin (staff, entered by hand). A signup is a claim: its status stays reported until a result bears it out and turns it confirmed, and it disappears if the squad withdraws. from_slug or to_slug is null only for staff joining or leaving; region filters on either squad.

Fair use

This runs on one small machine a long way from most of the internet. Cache what you fetch, honour the ETags, and prefer the live stream over polling. If you are building something that needs more than the limit allows, say so — that is a conversation, not a refusal.