Overlay data
If you'd rather build your own overlay than use the broadcast view, Draftify has two read-only JSON endpoints with every game's picks and bans.
- No key or login is needed.
- Both allow cross-origin requests (
Access-Control-Allow-Origin: *), so a browser source on your own page can fetch them. - Draft codes aren't case-sensitive.
- Please poll no more than once a second. Each game's
versionchanges whenever anything in it changes, so you can skip work when it hasn't moved.
The endpoints only have locked picks and bans. Selections in progress (hovers), the countdown, pauses, late requests and drafter names aren't included. If you need those, use the broadcast view, with bg=transparent to layer it over your own design. The draft room's live WebSocket connection isn't a public API and can change without notice.
The series: GET /api/series/{code}#
https://draftify.lol/api/series/k3qz9wmp4
{
"id": "k3qz9wmp4",
"team1Name": "Northwind",
"team2Name": "Kestrel",
"gameCount": 3,
"currentGame": 2,
"games": [
{ "number": 1, "status": "complete", "tab": "complete", "seated": [], "blueTeam": "team1" },
{ "number": 2, "status": "drafting", "tab": "current", "seated": ["blue", "red"], "blueTeam": "team2" },
{ "number": 3, "status": "lobby", "tab": "locked", "seated": [], "blueTeam": "team2" }
],
"championDataPatch": "16.20.1"
}
| Field | Meaning |
|---|---|
id |
The draft code. |
team1Name, team2Name |
Team names. Teams aren't tied to a side; see blueTeam. |
gameCount |
Games in the series (1–5). |
currentGame |
The first game that isn't complete, or the last game if all are. |
games[].status |
lobby, drafting, finalization or complete. See Game status. |
games[].tab |
How the series tab shows: current, complete, available or locked (the game before it isn't finished). |
games[].seated |
Sides that have a drafter in their seat. |
games[].blueTeam |
Which team is on Blue in that game: team1 or team2. |
championDataPatch |
The Data Dragon version the series uses for champions and art, for example 16.20.1. |
An unknown or expired code returns 404 with {"error": "not_found"}.
Picks and bans: GET /api/series/{code}/results#
https://draftify.lol/api/series/k3qz9wmp4/results
{
"id": "k3qz9wmp4",
"createdAt": 1791400000000,
"team1Name": "Northwind",
"team2Name": "Kestrel",
"gameCount": 1,
"mode": "normal",
"timerSeconds": 30,
"firstSelection": false,
"championDataPatch": "16.20.1",
"current": 1,
"games": [
{
"number": 1,
"status": "complete",
"version": 57,
"blueTeam": "team1",
"firstPick": "blue",
"pickOrderSet": false,
"stepIndex": null,
"seated": [],
"startedAt": 1791400100000,
"completedAt": 1791400700000,
"bans": {
"blue": [
{ "championId": 266, "locked": true },
{ "championId": 145, "locked": true },
{ "championId": 64, "locked": true },
{ "championId": 111, "locked": true },
{ "championId": 117, "locked": true }
],
"red": [
{ "championId": 897, "locked": true },
{ "championId": 222, "locked": true },
{ "championId": null, "locked": true },
{ "championId": 412, "locked": true },
{ "championId": 69, "locked": true }
]
},
"picks": {
"blue": [
{ "championId": 498, "locked": true, "role": "bot" },
{ "championId": 497, "locked": true, "role": "support" },
{ "championId": 113, "locked": true, "role": "jungle" },
{ "championId": 61, "locked": true, "role": "mid" },
{ "championId": 58, "locked": true, "role": "top" }
],
"red": [
{ "championId": 150, "locked": true, "role": "top" },
{ "championId": 234, "locked": true, "role": "jungle" },
{ "championId": 134, "locked": true, "role": "mid" },
{ "championId": 202, "locked": true, "role": "bot" },
{ "championId": 89, "locked": true, "late": true, "role": "support" }
]
}
}
]
}
Series fields#
| Field | Meaning |
|---|---|
createdAt |
When the draft was created (milliseconds since 1970, UTC). |
mode |
normal or fearless. |
timerSeconds |
30, 45, 60, or null when the timer is off. |
firstSelection |
true when the series uses Separate pick order. |
current |
The game a draft link without ?game= opens. |
games |
Every game of the series, in order. |
Game fields#
| Field | Meaning |
|---|---|
number |
1-based game number. |
status |
lobby, drafting, finalization or complete. |
version |
Goes up with every change to the game. |
blueTeam |
team1 or team2: who is on Blue this game. |
firstPick |
blue or red: the side that picks first. Always blue unless the series uses Separate pick order. |
pickOrderSet |
With Separate pick order, whether this game's pick order has been chosen. |
stepIndex |
While drafting: the current step, 0 to 19 (see Step order). Otherwise null. |
seated |
Sides with a drafter in their seat. |
startedAt, completedAt |
When the draft started and finished (milliseconds), or null. |
bans, picks |
Five slots per side: bans.blue[0] is Blue ban 1, picks.red[4] is Red pick 5. |
Slots#
| Field | Meaning |
|---|---|
championId |
Riot's numeric champion id (the key in Data Dragon), or null. |
locked |
false: the draft hasn't reached this slot yet. true with a championId: that champion was locked in. true with null: the slot was left empty, because the drafter locked None or the timer ran out. |
late |
true when the slot was filled with Late Selection. Absent otherwise. |
role |
Picks only: top, jungle, mid, bot or support. Present only once the game is complete, and only for picks the drafter gave a role (Role confirmation). Roles are hidden until then. |
Game status#
| Status | Meaning |
|---|---|
lobby |
Waiting for drafters to take their seats and ready up. |
drafting |
Picks and bans in progress. stepIndex says whose turn it is. |
finalization |
All 20 steps are done. Drafters can still undo, set roles or fill late slots for a short time. Picks can still change. |
complete |
Final. Roles are revealed. |
An approved undo can take a game from finalization back to drafting, and clear slots that were locked. Don't treat a locked slot as final until the game is complete.
Step order#
The 20 steps follow the standard tournament order. F is the side in firstPick and S is the other side:
Steps (stepIndex) |
Order |
|---|---|
| 0–5 | Bans: F S F S F S |
| 6–11 | Picks: F S S F F S |
| 12–15 | Bans: S F S F |
| 16–19 | Picks: S F F S |
Each side fills its own slots 1 to 5 in order. The full table with Blue first is on the Drafting page.
Champion names and images#
championId is Riot's numeric champion key. To get names and image file names, load Data Dragon's champion list for the series' patch:
https://ddragon.leagueoflegends.com/cdn/{championDataPatch}/data/en_US/champion.json
Each entry has key (the numeric id as a string), id (the name used in image URLs, for example MonkeyKing for Wukong) and name (the display name). The latest version is the first entry of https://ddragon.leagueoflegends.com/api/versions.json.
These are the image URLs Draftify itself uses. {id} is the Data Dragon id, and {skin} is a skin number (0 is the base skin):
| Image | URL |
|---|---|
| Square icon | https://ddragon.leagueoflegends.com/cdn/{patch}/img/champion/{id}.png |
| Loading-screen portrait (308×560) | https://ddragon.leagueoflegends.com/cdn/img/champion/loading/{id}_{skin}.jpg |
| Centred splash | https://ddragon.leagueoflegends.com/cdn/img/champion/centered/{id}_{skin}.jpg |
| Full splash | https://ddragon.leagueoflegends.com/cdn/img/champion/splash/{id}_{skin}.jpg |
| Icon fallback (CommunityDragon) | https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-icons/{championId}.png |
One quirk: Fiddlesticks' loading-screen and centred-splash files are named FiddleSticks, not Fiddlesticks. Skin numbers and names are in https://ddragon.leagueoflegends.com/cdn/{patch}/data/en_US/champion/{id}.json (entries with a parentSkin are chromas).
Example#
A minimal browser script that prints the current game's picks:
const code = "k3qz9wmp4";
const draft = await (await fetch(`https://draftify.lol/api/series/${code}/results`)).json();
const dd = await (
await fetch(`https://ddragon.leagueoflegends.com/cdn/${draft.championDataPatch}/data/en_US/champion.json`)
).json();
const byKey = new Map(Object.values(dd.data).map((c) => [Number(c.key), c]));
const game = draft.games[draft.current - 1];
const teamOn = (side) =>
(side === "blue") === (game.blueTeam === "team1") ? draft.team1Name : draft.team2Name;
for (const side of ["blue", "red"]) {
console.log(`${teamOn(side)} (${side})`);
for (const slot of game.picks[side]) {
const champ = byKey.get(slot.championId);
const label = champ ? champ.name : slot.locked ? "No pick" : "…";
const art = champ && `https://ddragon.leagueoflegends.com/cdn/img/champion/loading/${champ.id}_0.jpg`;
console.log(" ", label, slot.role ?? "", art ?? "");
}
}
To keep it live, fetch again every second or two, and redraw only when the game's version changes.
Result images#
Draftify also renders each draft as an image, the same one link previews show:
https://draftify.lol/api/series/{code}/card.png?game=2
It's a PNG, 1200 pixels wide. Without ?game=, a multi-game series with a finished game gets the whole-series card, and otherwise the current game's. Images of finished games are cached for a day, and live ones for 30 seconds.