Nameless Esports platform

Developer API

Read competitions, brackets, standings and circuits from Nameless Arena for your stream overlays, Discord bots and league sites. With a write key, reschedule matches, set results and save seeding from your own tools. Webhooks tell you when a match is final.

Base URL
https://namelessesports.com/api/v2
Format
JSON over HTTPS
Spec
openapi.json (OpenAPI 3.1)

Authentication

Public competitions and circuits can be read without a key. To read private events you can see, or to change anything, create a key in the Arena console under Developers and send it in the Authorization header.

curl "https://namelessesports.com/api/v2/competitions/your-league" \
  -H "Authorization: Bearer ne_live_..."
  • Read keys see what you can see on the site, including private competitions you run or play in.
  • Write keys can also reschedule matches, set results and save seeding, in competitions where you have that right in the console.
  • Keep keys secret. A key acts as you. Use it from a server, never in a public web page. You see a key once, when you create it; we store only a fingerprint. Revoke a key in the console and it stops working on its next request. You can hold up to 10 active keys.

Rate limits

Each key may make 120 read requests and 30 write requests a minute. Requests without a key share a per-IP limit across the whole site. Over the limit you get 429 with a Retry-After header giving the seconds to wait. If you are polling, every 30 seconds is plenty for a live bracket; webhooks are better for knowing when a match ends.

Responses and errors

Every success is wrapped in data. Lists add meta with nextCursor and limit. Errors carry a stable code and a message written for people. Dates are ISO 8601 in UTC. GET endpoints send Access-Control-Allow-Origin: * so public data can be read from a browser.

An error
{ "error": { "code": "not_found", "message": "No such competition." } }
400
The request body is invalid. The message says which field.
401
The key is unknown, revoked or expired, or a write was sent without one.
403
The key is read-only, or its owner lacks the right in that competition.
404
Not found, or not visible to you. Private events are never confirmed to exist.
409
The change conflicts with the event as it stands, such as moving a finished match.
415
A write was not sent with Content-Type: application/json.
429
Rate limited. Wait for Retry-After seconds.

Endpoints

Paths are relative to https://namelessesports.com/api/v2.

GET/competitions

Public. No key needed.

Public, announced competitions in the same order the Arena shows them: live first, then open for sign-ups, then upcoming, then finished.

  • Filters: q (text search), game (for example rocket-league), kind (tournament or league), region (na, sa, eu, mena, africa, asia, oce, global), mode (online or offline), status (open, live, upcoming or finished), fee (free or paid), size (team size).
  • Paging: limit (1 to 50, default 20) and cursor. Pass meta.nextCursor back as cursor for the next page; it is null on the last page.
Request
curl "https://namelessesports.com/api/v2/competitions?game=rocket-league&status=open&limit=10"
Response
{
  "data": [
    {
      "id": "cm2x...",
      "slug": "rush-weekly-42",
      "name": "Rush Weekly 42",
      "kind": "tournament",
      "status": "registration",
      "region": "na",
      "isOnline": true,
      "games": ["rocket-league"],
      "teamSize": 3,
      "entryFeeCents": 0,
      "startsAt": "2026-11-01T23:00:00.000Z",
      "community": { "slug": "rocket-rush", "name": "Rocket Rush" },
      "url": "https://play.namelessesports.com/rush-weekly-42",
      "logoUrl": null,
      "bannerUrl": null,
      "featured": false
    }
  ],
  "meta": { "nextCursor": "bzoxMA", "limit": 10 }
}

GET/competitions/{slug}

Public competitions need no key. Private ones and drafts return 404 unless the key's owner can see them.

Everything on the public competition page: seasons, stages, current standings for each stage, fixtures with their bracket links, and participants.

  • Up to 2000 fixtures and 500 participants are returned, the same limits as the public page.
  • Participants carry names, seeds, status and final placement. Contact details are never included.
  • To draw a bracket, group fixtures by stageId and follow winnerNextFixtureId from each match to the next.
Request
curl "https://namelessesports.com/api/v2/competitions/rush-weekly-42"
Response
{
  "data": {
    "id": "cm2x...", "slug": "rush-weekly-42", "name": "Rush Weekly 42", "status": "active",
    "games": [{ "id": "ct9a...", "game": "rocket-league", "label": "", "teamSize": 3, "defaultBestOf": 5 }],
    "seasons": [{ "id": "cs1b...", "name": "Main", "status": "active", "startsAt": "...", "endsAt": null }],
    "stages": [{ "id": "st4c...", "name": "Playoffs", "kind": "double_elim", "status": "active", "order": 0 }],
    "standings": [{ "stageId": "st4c...", "computedAt": "...", "rows": [ ... ] }],
    "fixtures": [
      {
        "id": "fx7d...", "stageId": "st4c...", "round": 1, "roundLabel": "Upper Quarterfinal",
        "matchNumber": 1, "bracketSide": "winners", "status": "completed",
        "scheduledAt": "...", "bestOf": 5, "scoreA": 3, "scoreB": 1, "winner": "A",
        "participantAId": "cp1...", "participantBId": "cp2...",
        "winnerNextFixtureId": "fx9e...", "streamUrl": null, "vodUrl": null
      }
    ],
    "participants": [{ "id": "cp1...", "name": "Orbit", "seed": 1, "status": "active", "finalPlacement": null }]
  }
}

GET/fixtures/{id}

Same visibility as its competition.

One match with both teams, per-game scores and the competition it belongs to.

Request
curl "https://namelessesports.com/api/v2/fixtures/fx7d..."
Response
{
  "data": {
    "id": "fx7d...", "status": "completed", "scoreA": 3, "scoreB": 1, "winner": "A",
    "participantA": { "id": "cp1...", "name": "Orbit", "seed": 1 },
    "participantB": { "id": "cp2...", "name": "Lowkey", "seed": 8 },
    "games": [{ "gameNumber": 1, "scoreA": 4, "scoreB": 2, "winner": "A", "map": null }],
    "stage": { "id": "st4c...", "name": "Playoffs", "kind": "double_elim" },
    "competition": { "slug": "rush-weekly-42", "name": "Rush Weekly 42", "url": "..." }
  }
}

PATCH/fixtures/{id}

Write key. The owner needs the right to manage the schedule.

Move a match, or set its stream and VOD links. Send any of scheduledAt, streamUrl, vodUrl and roundLabel; null clears a field.

  • A match that is under way or finished cannot be moved (409).
  • Moving a match tells both rosters and posts in the match chat, exactly as the console does.
Request
curl -X PATCH "https://namelessesports.com/api/v2/fixtures/fx7d..." \
  -H "Authorization: Bearer $NAMELESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scheduledAt":"2026-11-02T01:00:00Z","streamUrl":"https://twitch.tv/yourleague"}'
Response
{
  "data": {
    "fixture": { "id": "fx7d...", "scheduledAt": "2026-11-02T01:00:00.000Z", "streamUrl": "https://twitch.tv/yourleague", ... },
    "changes": { "moved": true, "streamAdded": true, "streamRemoved": false, "vodAdded": false, "vodRemoved": false, "labelChanged": false }
  }
}

POST/fixtures/{id}/result

Write key. The owner needs the right to override results.

Set a match result as an organizer. It is final straight away, with no confirmation step, and the bracket moves on.

  • scoreA and scoreB are games won in the series. games is optional; when sent, its game winners must add up to the series score.
  • The result is written to the competition's activity log, marked as coming through the API.
Request
curl -X POST "https://namelessesports.com/api/v2/fixtures/fx7d.../result" \
  -H "Authorization: Bearer $NAMELESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scoreA":3,"scoreB":1}'
Response
{
  "data": {
    "completed": true,
    "fixture": { "id": "fx7d...", "status": "completed", "scoreA": 3, "scoreB": 1, "winner": "A" }
  }
}

PUT/competitions/{slug}/seeding

Write key. The owner needs the right to manage participants.

Save the whole seeding order for one game. order lists participant ids, top seed first, and must include every active entrant exactly once.

  • titleId is the game's id from games[] in the competition response. Add divisionId for a division.
  • locked lists participants whose seed shuffles and auto-seeding must leave alone.
  • Refused once any match has started (409). Before that, brackets already drawn are re-slotted to the new seeds.
Request
curl -X PUT "https://namelessesports.com/api/v2/competitions/rush-weekly-42/seeding" \
  -H "Authorization: Bearer $NAMELESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{"titleId":"ct9a...","order":["cp1...","cp2...","cp3..."],"locked":["cp1..."]}'
Response
{ "data": { "saved": 3, "reslotted": 2 } }

GET/circuits/{slug}

Public. No key needed.

A circuit, its points table and size tiers, the approved events in it and the leaderboard. Add ?subleague=key for one subleague's board.

Request
curl "https://namelessesports.com/api/v2/circuits/rocket-rush-s2?subleague=na"
Response
{
  "data": {
    "circuit": { "slug": "rocket-rush-s2", "name": "Rocket Rush Season 2", "attribution": "team",
                 "pointsTable": [{ "from": 1, "to": 1, "points": 100 }], "tiers": [], "subleagues": [ ... ] },
    "subleague": "na",
    "events": [{ "slug": "rush-weekly-42", "name": "Rush Weekly 42", "entrants": 24, "counted": true }],
    "leaderboard": [{ "rank": 1, "name": "Orbit", "points": 340, "events": 4, "best": 1, "adjustment": 0 }]
  }
}

Writes follow the console's rules

The API does not have its own permissions. A write key acts as the person who created it, and every write runs the same checks and side effects as the Arena console: a moderator can set results but not reschedule, a caster can only read, and a result set through the API still moves the bracket on, updates standings, posts in the match chat and appears in the competition's activity log. If you lose a right in a competition, your keys lose it too.

Captains reporting and confirming their own scores is not offered through the API. Teams do that in the match room so the other side can confirm or dispute.

Webhooks

Instead of polling, register an https address and we will POST to it when something happens. Set endpoints up in the console under Developers, either for one competition or for every competition you run. Each endpoint gets a signing secret, shown once.

registration.created
A team signs up.
registration.approved
A sign-up is approved.
fixture.completed
A match is final, whether reported, confirmed, forfeited or set by an organizer.
stage.completed
A stage finishes.
competition.completed
The competition finishes.

Headers

  • X-Nameless-Event the event name, such as fixture.completed.
  • X-Nameless-Delivery the delivery id. It stays the same across retries, so use it to skip duplicates.
  • X-Nameless-Signature t=<unix seconds>,v1=<hex>, where v1 is the HMAC-SHA256 of <t>.<raw body> keyed with your endpoint secret. Reject timestamps more than five minutes old.

Retries

Answer with any 2xx status to accept a delivery. Anything else, or no answer, is retried after 1 minute, 5 minutes, 15 minutes, 1 hour, 3 hours, 6 hours, 12 hours and 24 hours, then dropped. Redirects are not followed. An endpoint that fails 25 deliveries in a row is switched off.

A fixture.completed payload
{
  "event": "fixture.completed",
  "occurredAt": "2026-11-02T02:14:09.000Z",
  "competition": { "id": "cm2x...", "slug": "rush-weekly-42", "name": "Rush Weekly 42", "status": "active" },
  "fixture": {
    "id": "fx7d...", "stageId": "st4c...", "round": 1, "roundLabel": "Upper Quarterfinal",
    "status": "completed", "scoreA": 3, "scoreB": 1, "winner": "A", "forfeitBy": null,
    "completedAt": "2026-11-02T02:14:09.000Z",
    "participantA": { "id": "cp1...", "name": "Orbit" },
    "participantB": { "id": "cp2...", "name": "Lowkey" }
  }
}
Verifying the signature in Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.NAMELESS_WEBHOOK_SECRET; // whsec_..., shown once when you create the endpoint

// Use the raw body: the signature covers the exact bytes we sent.
app.post("/hooks/nameless", express.raw({ type: "application/json" }), (req, res) => {
    const header = req.get("X-Nameless-Signature") ?? "";
    const parts = Object.fromEntries(header.split(",").map(p => p.split("=", 2)));
    const t = Number(parts.t);

    // Refuse anything signed more than five minutes ago, so a captured request cannot be replayed.
    if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return res.sendStatus(400);

    const expected = createHmac("sha256", SECRET).update(`${t}.${req.body}`).digest("hex");
    const a = Buffer.from(expected, "hex");
    const b = Buffer.from(parts.v1 ?? "", "hex");
    if (a.length !== b.length || !timingSafeEqual(a, b)) return res.sendStatus(400);

    const event = JSON.parse(req.body.toString("utf8"));
    const deliveryId = req.get("X-Nameless-Delivery"); // the same on every retry: skip ones you have seen
    console.log(deliveryId, event.event, event.competition.slug);

    res.sendStatus(200);
});

app.listen(3000);

Embeds

For a bracket or table on your own site with no code, use an iframe. Embeds link back to the full event in the Arena.

/embed/<slug>/bracket
The bracket. Add ?stage=<stageId> to pick a stage.
/embed/<slug>/standings
The standings table.
/embed/<slug>/register
A sign-up card that opens registration in the Arena.

All three take ?theme=light or ?theme=dark (the default), and ?title=0 hides the heading.

<iframe
  src="https://play.namelessesports.com/embed/rush-weekly-42/bracket?theme=light"
  width="100%" height="640" style="border:0" loading="lazy"
  title="Rush Weekly 42 bracket"></iframe>

OpenAPI

The full description, including webhook payloads and the key and webhook management endpoints the console uses, is at https://namelessesports.com/api/v2/openapi.json. Load it into Postman, Insomnia or a client generator.

Something missing that would make your tool possible? Tell us what you are building.