API docs lite
Derived aggregates only, one explicit patch per call, everything carries its games
The ko.lol API serves the desktop app and this site's build, and its aggregate routes are
open to read. This page is the whole public contract: the routes, their parameters, the
patch rule that trips everyone once, the cache windows, and how to cite what you fetch. The
base URL is https://api.ko.lol.
The contract in numbers
Each figure here is restated in full in its section below.
What every response carries
The store behind every route holds 108,616 recorded ranked games right now, and every response stamps generatedAt and its game counts, so staleness and thinness are visible in the document itself.
ko.lol publishes worked out aggregates only, never raw Riot match data. Estimates in these responses arrive with a 95% range, a games count and a provenance word, and cells under their floor arrive as null with the count so far. Quote the number with its games or you are quoting a different claim than the one we made.
The public GET routes 13 routes
Parameters mirror the fetchers this site itself is built with. Anything not listed here is not public.
| route | what it answers | parameters | patch |
|---|---|---|---|
GET /v1/status | Store counts, the list of patches with recorded games, and crawler state per region | none | no patch. This is where the patch list comes from |
GET /v1/tierlist | Champion and role rows: score, tier, win impact with range, games, pick rate, and the formula block with every constant | patch required; role optional (top, jungle, mid, bot, support; omit for all); queue optional (420 ranked solo default, 450 ARAM) | ?patch= required |
GET /v1/runes/{champion}/{role} | Whole recorded rune pages with games and wins, single-slot counts, and damage-type splits | champion key and role in the path | ?patch= required |
GET /v1/skills/order | Per-level skill shares, the modal max order, and the first-max comparison with its verdict | championKey, role, patch | ?patch= required |
GET /v1/jungle/path | Recorded jungle routes with camp times and a win delta per route | championKey | no patch. The route aggregates the whole store, and the response says so |
GET /v1/rdd | Patch-boundary item reports: effect in wins per 100 games, range, and games on each side | patch | ?patch= required |
GET /v1/bundle | Champions, items, item names and recorded builds for one patch. About 6.4 MB | patch | ?patch= required |
GET /v1/web/matchups/{champion} | Opponent rows with counts, shrunk estimates and verdicts. Sorted best matchup first, so the counters are the tail of the list | champion key in the path; patch required; role optional (omit for the most played role) | ?patch= required |
GET /v1/web/synergies/{champion} | Teammate rows ranked by the win rate delta against the champion's own average, with the same floors and formula block | champion key in the path; patch required; role optional | ?patch= required |
GET /v1/web/matchup/{a}/{b} | One exact pair, both sides: estimates, the head-to-head record, and the lane at minute 15 where measured | both champion keys in the path; patch required; role optional | ?patch= required |
GET /v1/web/data | The catalog of data explorer metrics that exist, the metrics that are not measured yet with the reason, and the pool disclosure | patch | ?patch= required |
GET /v1/web/data/{metric} | One explorer's rows with estimates, floors, coverage and caveats | metric id in the path (listed by /v1/web/data); patch required; role and champion optional where the catalog says they apply | ?patch= required |
GET /v1/web/profile/{platform}/{slug} | A built player profile, or one of five honest non-ready answers: pending, moved, not found, ambiguous, unavailable. The non-200 statuses carry a real JSON body | platform and slug in the path; the slug is the Riot ID in lowercase with # replaced by a hyphen | no patch. A profile is the player's recorded window, not one patch's cell |
The patch rule
The one mistake everybody makes once, written down so you make it zero times.
- Every route marked "?patch= required" answers HTTP 404 without it. There is no server side default on purpose: a default would quietly serve numbers from a patch nobody is playing.
- Get the patch list from https://api.ko.lol/v1/status, in the clickhouse patches array, each entry with its recorded games.
- Sort patch numbers as numbers, never as text. As text, "16.9" sorts above "16.14" and you build on the wrong patch. Split on the dot and compare the parts as integers. Do not use the latestPatch field in the status response for this: it is sorted as text and can point at an old patch.
- Worked examples for the current patch: https://api.ko.lol/v1/tierlist?patch=16.14 and https://api.ko.lol/v1/runes/Ahri/mid?patch=16.14.
Cache windows and pacing
What freshness to expect, and how to be a polite caller.
- /v1/status, /v1/tierlist, /v1/runes and /v1/rdd answer from live queries behind a cache of about 60 seconds.
- /v1/bundle is rebuilt about every 5 minutes and is the heaviest document here, about 6.4 MB for a patch.
- /v1/jungle/path and /v1/skills/order read tables refilled by batch jobs, so their freshness is the generatedAt stamp in the response, not the clock.
- The /v1/web/ routes are rate limited at 600 requests per minute. Past the limit you get 429, and a busy query engine can answer 503; both carry a Retry-After header. Sleep exactly what it says and retry. Our own build does the same and never needs more.
- Cache on your side by URL: the same URL with the same patch answers the same document until the store moves, and every response says when it was generated.
How to cite a number
The format that keeps the claim attached to its evidence.
- Cite as: ko.lol, patch 16.14, n games, page URL.
- Always carry the patch and the sample size with the number.
- Cells reading "not enough games yet", and null estimates with their counts, are not results. Do not quote them as results.
- Verdicts come in three words and there is no fourth: beats, too close, not enough games. A tie is an answer.
- Say whose games these are: recorded ranked games found by following Diamond players and the people they play with, on the regions the status route lists. It is not every rank and it is not every region.
The machine-readable version of this page, with the page family index for the website, is at /llms.txt.
What is not public
The line, drawn once.
Everything not in the table above needs a signed request. That class covers player profiles' compute, per-match analysis, recommendations and scouting: the key ships only inside the desktop app, no key is issued to third parties, and those routes are deliberately not documented here. Raw Riot match JSON is never served on any route, signed or not.
The numbers behind the signed class are published as HTML on this site, each page with its ranges, counts and structured data, so nothing readable is locked behind the signature. If a route here changes shape, the change lands on the changelog.