Read-only JSON with permissive CORS: any community site can build its own mix ELO or stats pages on top of the same data. No key needed for GET.
/api/usl-mix/gamesRecent games newest first, each with a full player list - alias, side, class, kills, deaths, shots fired / landed, accuracy, bio dart hits, the heal split, opening kills, per-weapon kills, and the captain / shotcaller / vocal flags. Enough to build league-wide aggregates without a detail fetch per game.
| limit | default 25, max 100 |
| offset | pagination offset |
| kind | mix | pub | test | all (default mix + pub) |
| map | map key, e.g. els, kp, apollo |
| alias | only games this player was in |
| since | ISO date lower bound |
| rated | legacy. Every mix is rated now, so kind=mix is the filter you want - rated=true additionally drops the handful of mixes played before the opt-in was retired |
curl "https://www.freeinf.org/api/usl-mix/games?kind=mix&limit=5"
/api/usl-mix/games/{id}One game in full: teams, every player row (kills, deaths, class time, shots fired / landed, accuracy, per-weapon hits, heals, weapon kills, rating change) and every kill event with weapon attribution.
| id | game UUID or the zone script match_id |
curl "https://www.freeinf.org/api/usl-mix/games/<uuid>"
/api/usl-mix/playersRating leaderboard (mix games) merged with career totals (mix + pub).
| limit | default 50, max 200 |
| minGames | minimum rated games, default 3 |
| sort | rating | games | kd | winrate | kills |
| q | alias search |
curl "https://www.freeinf.org/api/usl-mix/players?minGames=5&limit=20"
/api/usl-mix/players/{alias}One player: rating + peak, record, career K/D and accuracy, class / weapon / map breakdowns, full rating history, recent games.
| games | recent games to include, default 20 |
curl "https://www.freeinf.org/api/usl-mix/players/SomeAlias"
/api/usl-mix/insightsAggregates for charts: Titan vs Collective win rate overall and per map, class win rate / K-D / accuracy per class (and a class × map matrix), kills by weapon with LAW shrapnel rolled up to the LAW.
| map | restrict class/weapon stats to one map |
| kind | mix | pub | all |
curl "https://www.freeinf.org/api/usl-mix/insights?map=els"
/api/usl-mix/leadersTop players over a rolling window: per-player totals (kills, K/D, hits, accuracy, heals, opening kills, rating change), category leaders, the best player in every class, and single-game records (most kills / opening kills / hits, best accuracy, most healing in one game).
| period | week | month | year | all (rolling 7 / 30 / 365 days; default week) |
| kind | mix | pub | all |
| map | restrict to one map |
| minGames | games needed for the K/D, win-rate and per-class rankings; default depends on period: week 5, month 12, year 22, all 22 |
| board | flat mode, one ranked list: kills | kd | kills_per_game | win_rate | heal | hits | accuracy | opening_kills | rating_gain | class:<name> | record:<kills|opening_kills|hits|accuracy|heal> |
| limit | entries in flat mode, default 10, max 100 |
curl "https://www.freeinf.org/api/usl-mix/leaders?period=month" curl "https://www.freeinf.org/api/usl-mix/leaders?board=kills&period=week&limit=10" curl "https://www.freeinf.org/api/usl-mix/leaders?board=class:medic&period=all"
/api/usl-mix/ratingsCurrent rating, games and provisional flag for a batch of aliases, in the order asked. Unknown players come back at the base rating with 0 games. The zone calls this during the draft to show captains the team averages.
| a | alias, repeatable: ?a=G&a=THE (max 64) |
| aliases | comma list alternative |
| format | text - one line per alias: alias, rating, games (tab separated) |
curl "https://www.freeinf.org/api/usl-mix/ratings?a=G&a=THE%20MOUNTAIN"
/api/usl-mix/liveLive snapshot of every USL arena that reported in the last ~2.5 minutes: the player list grouped by team (side T/C from the '- T' / '- C' suffix, spec and np as their own teams), each player's class and whether they are spectating, captains, the game clock / score, the draft state, and the in-game ticker lines as a spectator sees them. Zones post once a minute; responses are cached 20s. Same shape as /api/live (which also carries CTF).
| fresh | seconds of silence tolerated before an arena is dropped, 30..600 (default 150) |
| empty | 1 to include arenas with nobody in them |
curl "https://www.freeinf.org/api/usl-mix/live"
# -> { success, generated_at, fresh_window_s, arenas: [ { game, zone, arena, map, players_total, players_playing,
# state: { running, mode, label, time_left_ms, score: [{ team, side, kills }] },
# tickers: [{ idx, text, remaining_cs }],
# teams: [{ name, side, kills, players: [{ alias, class, spec, captain? }] }],
# mix: { label, phase, team_size, captains, turn, pool, teams } | null, updated_at, age_s } ] }/api/usl-mix/ingestCalled by the USL zone script at Game.End. Requires the shared ingest key as x-api-key, Authorization: Bearer, or auth_key in the body. Re-posting the same match_id is a no-op (idempotent). action=test returns an echo without storing anything.
{
"action": "game_result", "schema_version": 1, "script_version": "1.0.0",
"match_id": "9d1f...", "zone_name": "USL - Megamaps", "arena_name": "Arena 1",
"level_file": "uslMegamap2.lvl", "map_key": "els",
"game_kind": "mix", "team_size": 8, "rated": true, "first_pick_team": "Bes - T",
"started_at": "2026-09-05T20:00:00Z", "ended_at": "2026-09-05T20:18:12Z",
"duration_seconds": 1092, "end_reason": "mercy",
"teams": [
{ "name": "Bes - T", "side": "T", "kills": 61, "deaths": 31, "result": "win", "captain": "Bes", "shotcaller": "Bes", "player_count": 8 },
{ "name": "Axi - C", "side": "C", "kills": 31, "deaths": 61, "result": "loss", "captain": "Axidus", "shotcaller": null, "player_count": 8 }
],
"players": [
{ "alias": "Bes", "side": "T", "team_name": "Bes - T", "result": "win",
"is_captain": true, "is_shotcaller": true, "is_vocal": true,
"primary_class": "Marine", "classes": { "Marine": 1080 },
"kills": 12, "deaths": 3, "team_kills": 0, "kills_scoreboard": 12, "deaths_scoreboard": 3,
"shots_fired": 240, "shots_landed": 96, "accuracy": 40.0, "bio_dart_hits": 0,
"heal_amount": 0, "heal_uses": 0, "play_seconds": 1092,
"weapon_kills": { "1004": { "name": "LAW", "count": 4 }, "1000": { "name": "Assault Rifle", "count": 8 } },
"weapon_deaths": {} }
],
"kill_events": [
{ "t": 15230, "killer": "Bes", "victim": "Axidus", "killer_side": "T", "victim_side": "C",
"killer_class": "Marine", "victim_class": "Medic",
"weapon_id": 1117, "weapon_name": "Shrapnel", "root_weapon_id": 1004, "root_weapon_name": "LAW",
"team_kill": false, "kill_type": "Player", "attribution": "matched", "x": 17120, "y": 23504 }
]
}*mix scrim.Individual ratings are not published. The API still returns rating, rating_delta and friends, but a public ladder was pushing mixes toward slow, play-not-to-lose games in a scene that logs on for casual organised play. If you are building on this data, please don't render per-player ratings - everything else (kills, classes, accuracy, heals, opening kills, hits) is fair game. Ratings exist to balance drafts, and nobody is ranked.
Constants live in src/lib/uslMix/elo.ts. GET /api/usl-mix/admin/recompute returns the live values.
*mix scrim in the zone), and zone admins can still flip it afterwards with POST /api/usl-mix/admin/set-rated { game_id, rated }, which replays all ratings. Filter on kind=mix rather than rated.*setmap), else the level file name.heal_medikit and bio_dart_heal carry the split, and bio_dart_hits the raw count. The MediKit half is the HP that was missing on nearby team-mates when a heal fired, so it is a potential figure; the dart half is exact. The same field means the same thing on every endpoint - don't add darts on again yourself.?sc) and vocal (?v) are self-declared in the zone and are badges only - they never touch ratings.