Ratings and rankings
Glicko-2, why the board sorts on a discounted rating, and the sample floors.
Two related surfaces: a team's rating over time, and the leaderboard built from it.
Rating history
GET /v1/{game}/teams/{id}/ratings returns {rating, rd, as_of} points, oldest-first so the
series plots directly. Default 30 points, max 200.
rating is Glicko-2. rd is the rating deviation — the model's uncertainty. It falls as a team
plays and rises during inactivity, so a returning team has a high rd and its rating should be read
as provisional.
The leaderboard
GET /v1/{game}/rankings?type=team|player over a rolling 3-month window.
Teams rank on glicko2_conservative — the rating discounted by twice its deviation:
value = rating − 2 × rdThat's the important design decision. A team with a high rating the model isn't confident about
sorts below an equally-rated team it is confident about. It rewards demonstrated form over a lucky
run, and it means an inactive team drifts down as its rd grows rather than sitting on a stale
rating forever.
Players rank on rating. Each row reports which via its metric field, so you never have to infer
it.
Sample floors
Entities below a minimum sample are omitted entirely rather than ranked on noise:
- Teams: at least 5 rated matches in the window.
- Players: at least 30 rated maps.
So absence from the board means "not enough evidence", not "ranked last".
Row fields
rank is 1-based board position. Sorting rows by value reproduces rank exactly.
Teams additionally get:
form— up to 5 recent results as["W","L",…], newest first.rank_delta— movement versus roughly 7 days ago. Positive means moved up.nullmeans the team wasn't on the board then, which is different from "didn't move" (0).external_rank— a separately-sourced world ranking where one exists, reported alongside for comparison. It is not used for ordering.
Sorting and paging
/rankings accepts sort=rank|value|name with a - prefix for descending, and is not
cursor-paginated — limit truncates the computed board.
Re-sorting changes the order rows come back in, never the rank each entity holds. So
?sort=-rank&limit=10 returns the bottom ten with their real ranks, rather than renumbering them
1–10.
Own rank on a detail route
A team or player detail row can carry own_rank, but only when you ask:
curl -H "Authorization: Bearer $ESPORTSODDS_API_KEY" \
"https://api.esportsodds.gg/v1/cs2/teams/natus-vincere?rank=1"It costs a window query over the whole board, so it is off by default. Without the parameter,
own_rank is null meaning "not requested" — not "unranked".