Generate an Esports Double-Elimination Bracket
Turn an ordered team list into a complete double-elimination tournament structure without drawing rounds by hand.
Run — free
The generator treats the list order as seeding, expands the field to the next power of two, assigns any required byes, and returns linked winners and losers brackets. Every later slot identifies the match and outcome that feeds it, while the grand final includes the conditional reset required when the losers-bracket finalist wins the first championship match. The result is deterministic and ready for tournament software, production overlays, registration workflows, or a clear bracket review before an esports event begins.
Prepare the seed list and understand byes
Enter team names from strongest seed to weakest seed. The first name is seed one, the second is seed two, and so on; the generator never rearranges that ranking randomly. Every name must contain visible text, and names must be unique without regard to letter case, because two indistinguishable entrants would make later match reporting ambiguous. A double-elimination field is easiest to represent at a power of two, so a list of six teams becomes an eight-slot bracket while a list of eight remains unchanged. Empty slots are represented as explicit bye objects rather than invented opponents. Canonical seed placement keeps the highest seeds apart for as long as results follow the ranking: seed one begins opposite the last slot, while the strongest seeds occupy separate regions of the bracket. A team paired with a bye advances operationally without playing, but the returned structure retains that first-round match slot so match identifiers and downstream references stay stable. This is especially useful when registration closes with an uneven field or a late withdrawal leaves the event below its intended size.
Read the winners and losers bracket links
The output separates winners_bracket and losers_bracket into numbered rounds. First-round winners matches contain seeded team records or bye records. Later matches contain source objects instead of team names because the participants depend on results: kind says whether the slot receives a winner or loser, match identifies the upstream match, and slot preserves the side of the new pairing. Winners-bracket losses feed the losers bracket at staged intervals. Between those drops, consolidation rounds reduce the surviving losers-bracket field before another group arrives from the winners side. This produces the familiar double-elimination rhythm in which a team is removed only after its second competitive loss. Match identifiers are deterministic, such as W2M1 for the first match of winners round two and L3M2 for the second match of losers round three. Applications can store results against those identifiers, resolve source references when a match finishes, and render the same topology in a compact list, a conventional bracket canvas, or a broadcast graphic. The structure describes progression; it deliberately does not predict winners or fabricate scores.
Handle the championship and integrate the result
The grand_final object contains both the initial championship match and a conditional reset match. GF1 pairs the undefeated winners-bracket finalist with the survivor of the losers bracket. If the winners-bracket finalist wins GF1, the event is complete because the other finalist has now lost twice. If the losers-bracket finalist wins GF1, both finalists have one tournament loss, so GF2 is required to decide the champion on equal elimination terms. The reset condition is written explicitly instead of silently assuming a single final, which prevents a common implementation error in tournament dashboards. To integrate the generated structure, save the complete response as the tournament definition, then attach scheduling, server, score, and status data in your own system using each match id as the stable key. Resolve a source only after its referenced match is final, and treat bye opponents as automatic advancement according to your event rules. The generator performs no network calls and retains no team list. Browser generation is free, while an API request uses the displayed $0.002 base price when you need repeatable automation in registration or event-management pipelines.
What you can do with it
Seed an online qualifier
Convert registration rankings into linked winners and losers rounds, even when the final entrant count requires byes.
Build a broadcast bracket
Use stable match ids and progression sources to drive overlays without manually reconnecting every round.
Initialize tournament software
Store the deterministic topology first, then add schedules, scores, maps, and match states in an event system.
FAQ
How many teams are required?
At least four unique, non-empty team names are required, and the accepted maximum is 64 teams.
How are teams seeded?
Input order is seed order. The first team receives seed one, and canonical placement separates leading seeds across the bracket.
What happens when the team count is not a power of two?
The field expands to the next power of two and the unused seeded slots become explicit byes.
Does the bracket include a grand-final reset?
Yes. GF2 is conditional and is played only when the losers-bracket finalist defeats the undefeated finalist in GF1.
Does this schedule match times or choose winners?
No. It returns tournament topology and progression references; scheduling, scores, and result decisions remain with your event system.
What does an API request cost?
The base price per API request is $0.002. You can also generate the bracket free in the browser.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/sports/esports-bracket-seed-matchups \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"teams":["Aurora","Blaze","Cyclone","Dragons","Eclipse","Falcons"]}'const res = await fetch("https://api.kit.forhosting.com/sports/esports-bracket-seed-matchups", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"teams": [
"Aurora",
"Blaze",
"Cyclone",
"Dragons",
"Eclipse",
"Falcons"
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/sports/esports-bracket-seed-matchups",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"teams": [
"Aurora",
"Blaze",
"Cyclone",
"Dragons",
"Eclipse",
"Falcons"
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/sports/esports-bracket-seed-matchups", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"teams":["Aurora","Blaze","Cyclone","Dragons","Eclipse","Falcons"]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"teams":["Aurora","Blaze","Cyclone","Dragons","Eclipse","Falcons"]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/sports/esports-bracket-seed-matchups", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"teams": [
"Aurora",
"Blaze",
"Cyclone",
"Dragons",
"Eclipse",
"Falcons"
]
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "sports.esports_bracket_seed_matchups",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |