Public API reference for xb.live / Insignia Stats. Most routes are anonymous; a few require a valid Insignia session (noted per endpoint).
All endpoints are relative to your site (e.g. https://insigniastats.live).
4d530064) or game name (URL-encoded where used in paths). Title IDs are stable identifiers across the platform.
Insignia Stats uses a separate auth service for login. To sign in on the website, use the Login page. Your account and password are managed by the Insignia auth service; the stats site does not store your password.
For integrating login into your own pages or apps, include the client SDK and use the InsigniaAuth class. The SDK persists the session in localStorage and exposes login, logout, and user data methods.
<script src="https://insigniastats.live/insignia-auth.js"></script>Constructor:
| Option | Description |
|---|---|
| apiUrl | Auth API base URL (default: https://auth.insigniastats.live/api) |
| storageKey | localStorage key for session (default: insignia_auth) |
| Method | Description |
|---|---|
| login(email, password) | Log in; returns { success, user, sessionKey }. Emits login event. |
| logout() | Log out and clear session. Emits logout event. |
| getUser() | Returns current user { username, email } or null. |
| isLoggedIn() | Returns true if a session key exists (does not verify server-side). |
| verifySession() | Verifies the session with the auth server; returns boolean. |
| getFriends() | Returns cached friends: { friends: [{ gamertag, status, isOnline, game?, duration?, lastSeen? }], lastUpdated, count }. When online: game, duration; when offline: lastSeen. |
| getGames() | Returns cached My Games Played: { games: [{ title, lastPlayed, iconUrl }], lastUpdated, count }. lastPlayed is e.g. "10 minutes ago". |
| getProfile() | Returns cached profile: { isOnline, status, game?, timeOnline?, gamesPlayed: [{ title, lastPlayed, iconUrl }], lastUpdated, count }. |
| refreshFriends() | Refreshes friends from Insignia; returns updated friends data. |
| refreshGames() | Refreshes games (from profile My Games Played); returns updated games data. |
| refreshProfile() | Refreshes profile from Insignia; returns updated profile. |
| getMutes() | Deprecated. Mutes no longer available from Insignia; returns empty list. |
| startAutoVerify(intervalMs) | Periodically re-verifies the session (e.g. every 5 minutes). |
on('login', fn), on('logout', fn), on('error', fn).
const auth = new InsigniaAuth({ apiUrl: 'https://auth.insigniastats.live/api' });
if (auth.isLoggedIn()) {
const user = await auth.getUser();
console.log('Logged in as', user.username);
} else {
await auth.login(email, password);
}
Per-user profile data for the site. :username is the Insignia site username (URL-encode when needed). These GET routes are public.
username in the response root (there is no separate nickname).{
"username": "PlayerOne",
"profile": {
"avatar_url": "...",
"discord_profile_url": "...",
"linked_gamertag": "...",
"xbox_verified": false,
"bio": "...",
"social_links": []
},
"playTime": { "totalMinutes": 0, "lastState": "offline", "currentGame": null, ... },
"achievementScore": 1200,
"achievementCount": 5,
"newUnlocks": [],
"leaderboardPlayerName": "GamertagOrUsername",
"leaderboardStats": { "leaderboardsOn": 0, "top10Count": 0 },
"usesExplicitXboxLiveGamertag": false
}
{
"score": 1200,
"achievements": [ ... ],
"groups": [ ... ]
}
{
"games": [ ... ],
"leaderboardSupplement": false,
"hasPlayTimeTracking": true
}
| Parameter | Type | Default | Description |
|---|---|---|---|
| limit | integer | 10 | Max entries (1β50) |
{
"playerName": "...",
"explicitXboxLiveGamertag": null,
"usingSiteUsernameFallback": true,
"scores": [ ... ]
}
Changing bio, avatar, linked gamertag, etc. requires a valid Insignia session. Send X-Session-Key as a header, or sessionKey in the JSON body.
avatar_url, avatar_hue, discord_profile_url, linked_gamertag, bio, sync_avatar_from_discord. Optional social_links (normalized list) when Discord is linked per server rules. Discord URL changes may trigger avatar/name sync from Discord. You cannot set a separate public display name; it is always your Insignia site username.400 β missing sessionKey, invalid social links, or bad Discord URL; 401 β invalid session; 403 β social links without verified Discord; 502 β Discord lookup failure in some cases.
Star ratings and optional comments left on user profiles. The displayed average uses a Bayesian-style prior (equivalent to 10 ratings at 5β ) so new profiles stay near 5 until real feedback arrives.
viewer contains their stars/comment (omit X-Session-Key for anonymous).| Parameter | Type | Default | Description |
|---|---|---|---|
| feedbackLimit | integer | 12 | Recent feedback rows (max 30) |
Optional session: header X-Session-Key or query sessionKey β used only to populate viewer when the signed-in user has already submitted feedback.
{
"average": 4.85,
"ratingCount": 3,
"effectiveVotes": 13,
"priorOnly": false,
"feedback": [
{ "raterUsername": "...", "stars": 5, "comment": null, "createdAt": "..." }
],
"viewer": null
}
When signed in and you have rated this profile, viewer is an object with stars, comment, createdAt; otherwise null.
| Field | Type | Description |
|---|---|---|
| sessionKey | string | Required unless using X-Session-Key header |
| stars | integer | 1β5 |
| comment | string | Optional; trimmed, max 500 characters |
400 β missing session key, invalid username, self-rating, or invalid stars; 401 β invalid session.
{
"success": true,
"reputation": { "average": 4.9, "ratingCount": 4, ... },
"viewer": { "stars": 5, "comment": null, "createdAt": "..." }
}
Site-wide achievement catalog and leaderboards (points total and recent unlocks).
is_hidden; the public catalog never exposes revealed_name / revealed_description (those appear only on a profile after the user unlocks the achievement).{
"achievements": [ ... ],
"groups": [ ... ],
"stats": {
"maxGamerscore": 12345,
"totalAchievements": 400,
"playersWithAchievements": 1200
}
}
| Parameter | Type | Default | Description |
|---|---|---|---|
| limit | integer | 25 | Max rows (1β100) |
{ "leaderboard": [ { "rank": 1, "username": "...", "totalScore": 1200, "achievementCount": 5 } ] }
achievementName includes the game label (e.g. Insignia Tourist β XBL Core or Be a WORM β Worms 4: Mayhem). Hidden achievements use the title Hidden Achievement with no spoiler details (no spoiler).| Parameter | Type | Default | Description |
|---|---|---|---|
| limit | integer | 10 | Max rows (1β100) |
{ "recent": [ { "username": "...", "unlockedAt": "...", "achievementId": 1, "achievementKey": "...", "achievementName": "...", "gameName": "...", "points": 10 } ] }
Site-wide stats scraped from the Insignia.live homepage.
| Parameter | Type | Default | Description |
|---|---|---|---|
| days | integer | 30 | Number of days of history (1β365) |
[
{ "date": "2025-02-01", "registered_count": 30028 },
{ "date": "2025-02-02", "registered_count": 30105 }
]
| Parameter | Type | Default | Description |
|---|---|---|---|
| days | integer | 1 | Number of days of history: 1 (last 24 hours) or 7 (last 7 days), up to 30 |
[
{ "recorded_at": "2025-02-13 12:00:00", "online_count": 48 },
{ "recorded_at": "2025-02-13 12:05:00", "online_count": 52 }
]
| Parameter | Type | Default | Description |
|---|---|---|---|
| days | integer | 1 | Number of days of history: 1 (last 24 hours) or 7 (last 7 days), up to 30 |
[
{ "recorded_at": "2025-02-13 12:00:00", "lobby_count": 12 },
{ "recorded_at": "2025-02-13 12:05:00", "lobby_count": 14 }
]
Live per-game online counts, historical series, and a compact summary for βall statsβ style widgets.
| Parameter | Type | Default | Description |
|---|---|---|---|
| period | string | today | today β last 24h; 7d β last 168h |
{
"period": "today",
"peakOnline": 48,
"peakOnlineAt": "...",
"peakLobbies": 12,
"peakLobbiesAt": "...",
"mostPlayedGame": { "name": "...", "titleId": "...", "peak": 42 },
"gamesWithActivityCount": 15,
"totalSnapshotCount": 288
}
{
"Game Name One": {
"name": "Game Name One",
"online": 42,
"titleId": "4d53006e",
"image": "https://...",
"publisher": "Microsoft Game Studios"
},
"Game Name Two": { ... }
}
| Parameter | Type | Description |
|---|---|---|
| titleId | string | Game title ID (e.g. 4d530064) |
| Parameter | Type | Default | Description |
|---|---|---|---|
| hours | integer | 48 | Hours of history to retrieve |
GET /api/title/4d530064/online-users/history?hours=24
gameName is URL-encoded (e.g. Halo%202).{
"titleId": "4d530064",
"name": "Halo 2",
"title": "Halo 2",
"image": "https://...",
"publisher": "Microsoft Game Studios",
"releaseDate": "2004-11-09"
}
gameName is URL-encoded.{
"titleId": "4d530064",
"gameName": "Halo 2",
"leaderboard_count": 120,
"total_entries": 500000,
"time_entries": 300000,
"score_entries": 200000
}
[
{
"id": 42,
"name": "Leaderboard Name",
"titleId": "4d530064",
"gameName": "Halo 2"
}
]
{
"titleId": "4d530064",
"gameName": "Halo 2",
"leaderboardId": "42",
"name": "Leaderboard Name",
"entries": [
{
"rank": "1",
"name": "PlayerName",
"rating": "1000000",
"timeMs": 120000,
"timeFormatted": "2:00.000",
"isTime": true
}
]
}
Recent leaderboard entry changes (new, updated, or removed).
| Parameter | Type | Description |
|---|---|---|
| limit | integer | Max number of changes to return |
| offset | integer | Offset for pagination |
| game | string | Filter by game name |
["Halo 2", "Project Gotham Racing 2", ...]
days (1β365, default 30).days (default 30), optional limit to cap rows.{
"titleId": "4d53004b",
"gameName": "Project Gotham Racing 2",
"matches": [
{
"hostName": "PlayerName",
"currentPlayers": 8,
"maxPlayers": 8,
"timestamp": "2025-02-13 12:00:00"
}
]
}
/api/title/:titleId/matches). Path uses the same hex title ID as other /api/title/... routes.hours (default 24)./api/title/:titleId/matches or /api/game/:titleId/matches.hours. Prefer /api/title/:titleId/matches/history./api/title/:titleId/matches/stats.Search leaderboard data by player, time, rating, or structured queries.
time (required), tolerance (ms, optional).{
"searchTime": "1:23.456",
"searchTimeMs": 83456,
"tolerance": 0,
"results": [ ... ],
"count": 0
}
names β comma-separated list (max 20).players β array of { playerName, rank }.tolerance (ms), titleId (optional).titleId (optional).Endpoints for homebrew games (e.g. the original Xbox "Test Game") to submit player scores tied to a player's Insignia account, and for the public to read the resulting leaderboards. These power the Homebrew Leaderboards page and the xb.live Homebrew SDK.
The original Xbox runs open homebrew, so the API cannot simply trust a number. Instead a score submission carries the run's seed and a compact input log, and the server re-simulates the entire run with the identical deterministic engine. The score is accepted only if the replay reproduces it exactly. This is layered with:
| Layer | What it stops |
|---|---|
| HMAC-SHA256 app signature | Raw curl forgery β every run/start and score call is signed with the game's secret (issued on approval). |
| Single-use run nonce + server seed | Replay/duplicate submissions and pre-baked runs. The seed is chosen by the server at run/start, so players can't shop for a lucky seed. |
| Deterministic replay | Fabricated scores β a number with no valid input log (or a log that doesn't reproduce it) is rejected. |
| Plausibility & rate limits | Impossible timings and submission floods. |
Only replay-verified scores are ranked on the public board. See the SDK repo's docs/anti-cheat.md for the full design and its honest limitations.
Approved games receive a game_secret. Build a lowercase-hex HMAC-SHA256 over a canonical string:
// run/start
sig = HMAC_SHA256(game_secret, "start|" + game_id)
// score (input_hash = SHA256_hex(input_log))
msg = "score|" + game_id + "|" + run_id + "|" + nonce + "|" +
board_id + "|" + score + "|" + seed + "|" + input_hash
sig = HMAC_SHA256(game_secret, msg)
// achievements (input_hash = SHA256_hex(input_log))
msg = "ach|" + game_id + "|" + run_id + "|" + seed + "|" + input_hash
sig = HMAC_SHA256(game_secret, msg)
The player's session is supplied in the X-Session-Key header (from Insignia login).
X-Session-Key and a valid app signature. Returns a single-use nonce and the seed the game must play.{ "game_id": "og-testgame", "sig": "<hmac hex>" }
{
"success": true,
"run_id": "<hex>",
"nonce": "<hex>",
"seed": 123456789,
"server_time": 1733250000,
"ttl": 1800
}
X-Session-Key. The server checks the signature, the nonce (fresh/unused/this-user), that seed matches the issued run seed, then replays seed+input_log and accepts only if the recomputed score matches. Keep-best per player.{
"game_id": "og-testgame",
"run_id": "<hex>",
"nonce": "<hex>",
"board_id": "survival-0",
"score": 1234,
"seed": 123456789,
"input_hash": "<sha256 hex of input_log>",
"sig": "<hmac hex>",
"input_log": "0:0,40:2,95:1,..."
}
The input_log is a sparse, ascending list of frame:state changes (state bitmask: 1=left, 2=right). See the SDK's sim/sim-spec.md.
Leaderboards are split per game mode and map. Board ids follow "<mode>-<mapId>" (e.g. survival-0, battle-3, sprint-2). Single-player runs are Survival and must post to survival-<mapId> matching the run's map; a mismatched-map or battle-*/sprint-* board (those are written server-side from multiplayer rounds) is rejected with 400.
{ "success": true, "accepted": true, "verified": true,
"improved": true, "score": 1234, "rank": 1,
"message": "Verified! New best: 1234" }
Rejections: 401 bad signature/session, 400 nonce/seed/hash mismatch, 409 run already submitted, 410 run expired, 422 score failed replay verification or implausible timing.
| Parameter | Type | Default | Description |
|---|---|---|---|
| limit | integer | 100 | Max entries (1β1000) |
| offset | integer | 0 | Pagination offset |
| unverified | 0|1 | 0 | Set to 1 to include unverified scores |
{
"game_id": "og-testgame", "board_id": "highscore",
"name": "Survival Time", "sort": "desc", "unit": "frames",
"entries": [ { "rank": 1, "name": "Player", "score": 1234, "verified": true } ],
"total": 1, "limit": 100, "offset": 0, "hasMore": false
}
seed+input_log and unlocks only the achievements its own simulation proves (run-derived) or its own counters prove (cumulative). A forged request with no matching gameplay unlocks nothing. Requires X-Session-Key; idempotent (re-earning never duplicates).{
"game_id": "og-testgame",
"run_id": "<hex>",
"seed": 123456789,
"input_hash": "<sha256 hex of input_log>",
"sig": "<hmac hex, see ach signing above>",
"input_log": "0:0,40:2,95:1,..."
}
{
"success": true,
"stats": { "score": 1653, "obstaclesPassed": 42 },
"unlocked": [ { "id": "survivor_10s", "name": "Getting the Hang of It",
"description": "Survive 10 seconds in a single run.",
"kind": "run" } ],
"all": [ "survivor_10s", "survivor_20s", "dodger_25", "first_run" ]
}
unlocked lists only achievements newly granted by this call (clients toast these β especially cumulative ones the client can't predict). all is the player's full unlocked set for the game.
401 bad signature/session, 400 hash/seed mismatch or malformed log, 403 run belongs to another user.
X-Session-Key is supplied, each entry includes whether the caller has unlocked it. kind is "run" (earned in a single run, replay-verified) or "cumulative" (earned from server-tracked account history).{
"game_id": "og-testgame", "game_name": "Test Game",
"achievements": [
{ "id": "survivor_10s", "name": "Getting the Hang of It",
"description": "Survive 10 seconds in a single run.",
"kind": "run", "unlocked": true, "unlocked_count": 3 }
]
}
X-Session-Key). Used by the dashboard "Homebrew" view.map_id and is replayed on that map. Response: { game_id, maps: [ { map_id, name } ] }./api/hb/presence)A lightweight heartbeat so the site and game can show who/how many players are online. A player counts as online while their last heartbeat is within the window (90s). The heartbeat requires X-Session-Key and is HMAC-signed with "presence|" + game_id, so a client can only mark itself online — the count can't be inflated with fake users.
{ game_id, status?, sig } where status is online/playing/lobby. Returns { success, online_count, window_sec }.{ game_id, window_sec, online_count, players: [ { name, status, idle_sec } ] }./api/hb/mp/*)Shared-seed competitive rooms for up to 32 players: everyone plays the same server-issued seed + map, pushes live progress, and submits a replay-verified final score (same anti-cheat as the leaderboard). Lobby-mutating calls require X-Session-Key and are HMAC-signed with "mp|" + game_id; the round finish is signed like a score with board id mp.
Game modes. A lobby has a mode — survival (longest run wins), battle (last alive wins, elimination view), or sprint (clear the finish line at target frames). All modes use the same replay-verified run; they differ in win condition + presentation. The lobby object also reports alive_count, winner (once finished), and per-player px (live position for opponent ghosts) and cleared (sprint).
{ game_id, map_id?, mode?, name?, sig }. Returns { success, lobby }.status = waiting (default), playing, or all (for a live-match list). Each row includes mode.players is sorted by score; per-member run_id/nonce are never exposed here.{ sig }. Allowed even mid-round: the new member spectates the active round (spectating:true, not counted alive / for completion) and plays the next one.{ sig }.waiting so the same group can play again (clears the round seed + each member's run state; members stay). Idempotent. Any member may call it.{ map_id }.{ mode } (survival/battle/sprint).seed and issues a run per member. Returns the caller's { seed, map_id, run_id, nonce, lobby }.run_id/nonce are per-member secrets). Returns { run_id, nonce, seed, map_id, status, lobby }.{ frame, score, px, alive }; returns current standings. Cosmetic only (px drives opponent ghosts) β never used to award anything.{ score, seed, input_hash, sig, input_log }. When all members finish, the lobby becomes finished.board_id, name, sort, unit).X-Session-Key). Used by the dashboard "Homebrew Scores" view.X-Session-Key). Body: { game_id, name, description?, verify_mode? }. Returns a pending record; an admin then approves it and issues the one-time game_secret.game_secret (store it now; bake it into the game binary). Also creates a default board if none exist.{ board_id, name, sort: "desc"|"asc", unit? }.Endpoints may return:
{
"error": "Error message description"
}
Common HTTP status codes:
There are no rate limits on the public API. Please use it responsibly.