Public competitions API
Two read-only JSON endpoints for community tools: list the competitions that are open or in progress, then fetch one competition's fighter and map pool. They need no API key, no account and no headers, so a plain HTTP GET works from anywhere, including a Tabletop Simulator mod.
| Endpoint | Returns |
|---|---|
GET /api/v1/public/competitions | Competitions open for entry or in progress |
GET /api/v1/public/competitions/{id}/pool | That competition's fighters and maps |
Base URL: https://www.tabletopleague.com. The machine-readable contract is in
the interactive reference under Public.
List competitions
curl "https://www.tabletopleague.com/api/v1/public/competitions?game=unmatched"
| Param | Description |
|---|---|
game | Optional. A game slug, such as unmatched. Omit it for every game. An unknown slug returns 404. |
organization | Optional. An organization slug, the part after /o/ in its page address. Lists only that organizer's competitions. |
status | Optional. published (open for registration) or active (in progress). Omit it for both. |
The three params combine, and a competition has to match all of them.
{
"success": true,
"data": {
"competitions": [
{
"id": "3f1c2b9e-7d4a-4c1e-9b2f-5a6d7e8f9012",
"name": "Szeged Gambit V. Forduló",
"slug": "szeged-gambit-v-fordulo",
"status": "ACTIVE",
"type": "LADDER",
"format": "TOURNAMENT",
"start_date": "2026-10-01T00:00:00+00:00",
"end_date": null,
"url": "https://www.tabletopleague.com/o/szeged-gambit/szeged-gambit-v-fordulo",
"organization": { "name": "Szeged Gambit", "slug": "szeged-gambit" },
"game": { "name": "Unmatched", "slug": "unmatched" }
}
]
},
"meta": { "request_id": "…", "timestamp": "…" },
"_meta": { "request_id": "…", "timestamp": "…", "terms_url": "/api-terms" }
}
One organizer's competitions
To list only the official events, pass the organizer's slug. Unmatched's
official events are all run by one organization, unmatched-tournament:
curl "https://www.tabletopleague.com/api/v1/public/competitions?organization=unmatched-tournament"
Add &status=published for only the events still open for registration. The
slug is not case-sensitive.
- An organization slug that does not exist returns
404(NOT_FOUND), the same as an unknowngame. - A public organization that has nothing open or in progress returns
200with"competitions": []. An organization that isn't publicly visible returns404, the same as one that doesn't exist. Treat an empty list as "nothing to show right now", not as an error.
name is the competition's display title exactly as the organizer wrote it,
accents included. Competitions in progress (ACTIVE) come first, then those
open for registration (PUBLISHED), each group by start date. The list holds
at most 200.
Get a competition's pool
curl "https://www.tabletopleague.com/api/v1/public/competitions/3f1c2b9e-7d4a-4c1e-9b2f-5a6d7e8f9012/pool"
{
"success": true,
"data": {
"competition": {
"id": "3f1c2b9e-7d4a-4c1e-9b2f-5a6d7e8f9012",
"name": "Szeged Gambit V. Forduló"
},
"fighters": [
{ "id": "…", "name": "Bigfoot", "slug": "bigfoot", "children": [] },
{
"id": "…",
"name": "Yennefer & Triss",
"slug": "yennefer-triss",
"children": [
{ "id": "…", "name": "Triss", "slug": "triss" },
{ "id": "…", "name": "Yennefer", "slug": "yennefer" }
]
}
],
"maps": [
{ "id": "…", "name": "Sherwood Forest", "slug": "sherwood-forest" }
]
}
}
fightersare what a player picks in this competition. Fighters the organizer restricted, and retired ones that are no longer legal here, are left out.childrenlists the playable characters of a composite pick: a player picks "Yennefer & Triss" and plays Yennefer and Triss. It is empty for an ordinary fighter. Match your objects on either level.mapscan be empty, for a competition without a map pool or a game without maps.- Both lists are sorted by name. The
idvalues are stable and are the same ids the site uses everywhere else.
Which competitions you can see
- The list shows exactly what the public competitions page shows. Draft, unlisted, members-only and deleted competitions never appear, and neither do friendlies or play groups.
- The pool works for any competition you have the id of, as long as it is
not a draft, not members-only and not deleted. That includes unlisted
competitions and finished ones. Anything else returns
404, with no hint of which case it was.
Errors, rate limit and caching
Errors use the same envelope with success: false and an error.code:
VALIDATION_ERROR (400, a bad status, game, organization or id),
NOT_FOUND (404),
RATE_LIMITED (429) or INTERNAL_ERROR (500).
- Rate limit: 60 requests per minute per IP address, for each endpoint.
Beyond it you get
429withX-RateLimit-*headers. - Caching: successful responses are cached for up to 5 minutes. A new competition or a status change (opening registration, starting, finishing) shows up in the list within about 5 minutes. A fighter or map rename shows up in the pool within about 5 to 10 minutes. Errors are never cached.
Example: Tabletop Simulator (Lua)
Fetch the list, then the pool of the competition a player picked:
local BASE = "https://www.tabletopleague.com/api/v1/public/competitions"
function loadCompetitions()
WebRequest.get(BASE .. "?game=unmatched", function(request)
if request.is_error then
print("Request failed: " .. request.error)
return
end
local body = JSON.decode(request.text)
for _, competition in ipairs(body.data.competitions) do
print(competition.status .. " " .. competition.name .. " " .. competition.id)
end
end)
end
function loadPool(competitionId)
WebRequest.get(BASE .. "/" .. competitionId .. "/pool", function(request)
if request.is_error then return end
local pool = JSON.decode(request.text).data
for _, fighter in ipairs(pool.fighters) do
print("Fighter: " .. fighter.name)
for _, child in ipairs(fighter.children) do
print(" plays as: " .. child.name)
end
end
for _, map in ipairs(pool.maps) do
print("Map: " .. map.name)
end
end)
end
Stability
These endpoints only grow.
New fields may be added to any object. No field will be renamed, retyped or
removed without a new /v2 path, so ignore fields you do not
recognise. See the API Terms for acceptable use.