v1Developers
Galactic Leaderboard API
Show Ultra War’s Galactic Leaderboard on your own site, stream overlay or Discord bot. Free, read-only JSON: no key, no sign-up, open CORS.
- No key
- JSON
- CORS open
- Cached 60 s
- Stable v1
Endpoints
GEThttps://ultrawar.io/api/v1/leaderboard
One page of a board: ranks, names and numbers.
GEThttps://ultrawar.io/api/v1/leaderboard/boards
Every board with its name, description, unit and periods, plus the factions: build your menus from it.
Parameters
| Name | Default | Values |
|---|---|---|
board | renown | A board id (below, or /boards). |
period | the board’s first | week (from Monday 00:00 UTC) or all. |
faction | all | all or solari, hegemony, nereid, fungus, synthari, spacers, veilborn, gobbos. |
limit | 25 | How many rows, 1 to 100. |
offset | 0 | Rows to skip, 0 to 1000 (for paging). |
Anything else, or a parameter given twice, is a 400. Leave out a parameter to get its default.
Boards
| id | Board | value counts | Periods |
|---|---|---|---|
renown | Renown | Renown | week all |
gc | Credits earned | GC | week all |
pirates | Pirates downed | pirates | week all |
ore | Ore sold | Ore | week all |
charted | Systems charted | systems | week all |
station | Colony Station | GC dues extra: Colony Station tier | week all |
standing | Faction standing | standing | all |
victories | Skirmish victories | victories extra: victories at Standard | week all |
front | Front contribution | war score | week all |
worlds | Worlds taken | worlds | week all |
Every board counts only what the game’s servers checked themselves. How each is counted is in description from /boards.
Example
GET https://ultrawar.io/api/v1/leaderboard?board=pirates&period=week&limit=3{
"v": 1,
"board": "pirates",
"period": "week",
"faction": "all",
"limit": 3,
"offset": 0,
"refreshedAt": "2026-10-05T14:07:00.000Z",
"weekStart": "2026-10-05",
"total": 214,
"unit": "pirates",
"extra": null,
"rows": [
{
"rank": 1,
"name": "Halcyon",
"label": "Halcyon",
"faction": "solari",
"value": 48,
"extra": null,
"valueText": "48 pirates"
},
{
"rank": 2,
"name": null,
"label": "A Nereid Star Captain",
"faction": "nereid",
"value": 41,
"extra": null,
"valueText": "41 pirates"
},
{
"rank": 3,
"name": "Scrapjaw",
"label": "Scrapjaw",
"faction": "gobbos",
"value": 39,
"extra": null,
"valueText": "39 pirates"
}
],
"factionTotals": {
"solari": {
"value": 412,
"players": 31
},
"nereid": {
"value": 388,
"players": 27
}
},
"attribution": "Data from Ultra War (https://ultrawar.io)",
"docs": "https://ultrawar.io/developers/"
}Fields
v- Always 1 in this version.
board, period, faction, limit, offset- The query as answered, defaults filled in.
refreshedAt- When the standings were last drawn up (ISO 8601, UTC). They refresh every hour.
weekStart- The Monday (UTC) that starts the current week, YYYY-MM-DD.
total- How many Star Captains are ranked on this board, period and faction.
unit, extra- What value counts, and what extra holds (null when the board has none).
rows[].rank- The rank; ties share one. With a faction filter, the rank within that faction.
rows[].name- The player’s name as the game shows it, or null when they keep it hidden.
rows[].label- What to show: the name, or “A Solari Star Captain” for a hidden name.
rows[].faction- The faction id, or null.
rows[].value, rows[].extra- The numbers (extra: a Colony Station tier or Standard victories; else null).
rows[].valueText- The value as the game words it, e.g. “48 pirates”.
factionTotals- Per faction: the board added up (value; an average for Faction standing) and how many players.
attribution, docs- The credit line to show, and this page.
New fields and boards may be added in v1: ignore what you do not know.
Code
curl "https://ultrawar.io/api/v1/leaderboard?board=renown&period=week&limit=10"const url = new URL('https://ultrawar.io/api/v1/leaderboard');
url.search = new URLSearchParams({ board: 'pirates', period: 'week', limit: '10' });
const res = await fetch(url);
const data = await res.json();
if (!res.ok) throw new Error(data.error + ': ' + data.message);
for (const row of data.rows) console.log(row.rank, row.label, row.valueText);
console.log(data.attribution);// /leaderboard [board] [period]: a minimal discord.js v14 slash command
import { Client, GatewayIntentBits, EmbedBuilder, escapeMarkdown } from 'discord.js';
const API = 'https://ultrawar.io/api/v1/leaderboard';
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.on('interactionCreate', async (i) => {
if (!i.isChatInputCommand() || i.commandName !== 'leaderboard') return;
const q = new URLSearchParams({ board: i.options.getString('board') ?? 'renown', limit: '10' });
const period = i.options.getString('period'); // week or all; leave it out for the board's default
if (period) q.set('period', period);
const res = await fetch(API + '?' + q);
const data = await res.json();
if (!res.ok) return i.reply({ content: data.message, ephemeral: true });
const lines = data.rows.map((r) => `**${r.rank}.** ${escapeMarkdown(r.label)} · ${r.valueText}`);
await i.reply({ embeds: [new EmbedBuilder()
.setTitle(`Galactic Leaderboard · ${data.board} (${data.period})`)
.setDescription(lines.join('\n') || 'No Star Captains ranked yet.')
.setFooter({ text: data.attribution })] });
});
client.login(process.env.DISCORD_TOKEN);Caching and limits
- The standings refresh hourly. Answers are cached for 60 seconds at the edge (and may be served up to 5 minutes stale while they refresh), so polling more than once a minute gains nothing. Every few minutes is plenty for a bot or overlay.
- Conditional requests: answers carry an
ETagandLast-Modified; sendIf-None-Matchto get a 304 when nothing changed. - Rate limit: about 120 uncached requests a minute per address. Over it you get a 429 with
Retry-After. - CORS:
Access-Control-Allow-Origin: *on GET, HEAD and OPTIONS. No cookies or credentials are used or accepted.
Errors
Errors are JSON too: {"v":1,"error":"invalid-limit","message":"…","param":"limit"}.
| Status | error | Meaning |
|---|---|---|
| 400 | unknown-param | A parameter the endpoint does not take (me is game-only and never answered here). |
| 400 | duplicate-param | The same parameter twice. |
| 400 | unknown-board | Not a board id from /boards. |
| 400 | invalid-period | period is not week or all. |
| 400 | period-unavailable | The board has no such period (Faction standing is all time only). |
| 400 | invalid-faction | Not all or a faction id. |
| 400 | invalid-limit | limit is not a whole number from 1 to 100. |
| 400 | invalid-offset | offset is not a whole number from 0 to 1000. |
| 404 | not-found | No such endpoint. |
| 405 | method-not-allowed | Only GET, HEAD and OPTIONS. |
| 429 | rate-limited | Too many uncached requests from your address; wait for Retry-After seconds. |
| 502 | unavailable | The leaderboard could not be read just now; try again shortly. |
| 503 | not-open | The standings are not available yet. |
Privacy, credit and stability
- Privacy: a row names a Star Captain only if they show their name in the game (Settings › Privacy). Everyone else is “A Solari Star Captain” (their faction). Player ids are never in the API, and players kept off the boards never appear. There is no way to look up a player’s own rank: that stays in the game.
- Credit: Please credit “Data from Ultra War” with a link to ultrawar.io wherever you show it. Every answer carries the line in
attribution. - Stability: v1 keeps its fields and their meanings. We may add fields, boards or factions; anything that would break a client goes to a new version (
/api/v2/…), announced on the Devblog, with v1 kept running alongside it for a good while. - Fair use: fan sites, overlays and bots are welcome. Please don’t present your project as an official Ultra War one.
- Questions or ideas: devops@ultra-labs.io.