Appearance
swgoh
96 endpoints at a glance
| Method | Path | Summary |
|---|---|---|
GET | /abilities/ | List Abilities |
GET | /abilities/{ability_id}/ | Get Ability |
GET | /characters/ | List Characters |
GET | /characters/{base_id}/unlock-requirements/ | Character Unlock Requirements |
GET | /characters/{character_id}/ | Get Character |
GET | /characters/names/ | Character Names |
GET | /conquests/ | List Conquests |
GET | /conquests/{conquest_id}/ | Get Conquest |
GET | /events/ | List Events |
GET | /events/{event_id}/ | Get Event |
GET | /events/{event_id}/{instance_id}/ | Get Event Instance |
GET | /game-data/{type_}/ | List Items |
GET | /game-data/{type_}/{item_id}/ | Get Item |
GET | /game-data/types/ | List Types |
GET | /gear/ | List Gear |
GET | /gear/{gear_id}/ | Get Gear |
GET | /guilds/ | Get Guilds |
GET | /guilds/{guild_id}/ | Get Guild |
GET | /guilds/{guild_id}/engagement/activity/ | Get Guild Activity |
GET | /guilds/{guild_id}/engagement/contributions/ | Get Guild Contributions |
GET | /guilds/{guild_id}/history/ | Get Guild History |
GET | /guilds/{guild_id}/mods/summary/ | Get Guild Mods Summary |
POST | /guilds/{guild_id}/refresh-players/ | Refresh Guild Players |
GET | /guilds/{guild_id}/scoreboard/ | Get Guild Scoreboard |
GET | /guilds/{guild_id}/speed-sweep/ | Get Guild Speed Sweep |
GET | /guilds/{guild_id}/team-readiness/ | Guild Team Readiness Summary |
GET | /guilds/{guild_id}/team-readiness/{team_id}/ | Guild Team Readiness Detail |
GET | /guilds/{guild_id}/territory-battle/ | Get Guild Territory Battle |
GET | /guilds/{guild_id}/territory-wars/ | Get Guild Territory Wars |
GET | /guilds/{guild_id}/unit-ownership/{base_id}/ | Guild Unit Ownership |
GET | /guilds/{guild_id}/unlock-readiness/ | Guild Unlock Readiness Overview |
GET | /guilds/{guild_id}/unlock-readiness/{base_id}/ | Guild Unlock Readiness |
GET | /guilds/history/counts/ | Get History Counts |
GET | /guilds/search/ | Search Guilds |
GET | /leaderboards/ | Get Leaderboard |
GET | /leaderboards/metrics/ | List Metrics |
GET | /leaderboards/recent-unlocks/ | Recent Unlocks |
GET | /lightspeed/tokens/ | List Tokens |
GET | /lightspeed/tokens/{token_id}/ | Get Token |
GET | /me/follows/ | List Follows |
DELETE | /me/follows/{player_id}/ | Unfollow Player |
POST | /me/follows/{player_id}/ | Follow Player |
GET | /me/notifications/ | List Notifications |
POST | /me/notifications/seen/ | Mark Notifications Seen |
GET | /mods/recommendations/ | List Recommendations |
GET | /mods/recommendations/{recommendation_id}/ | Get Recommendation |
GET | /players/ | Get Players |
GET | /players/{code}/ | Get Player |
GET | /players/{player_id}/activity/ | Get Player Activity |
GET | /players/{player_id}/arena-defense/ | Get Player Arena Defense |
GET | /players/{player_id}/gac/bracket/ | Get Player Gac Bracket |
POST | /players/{player_id}/gac/bracket/refresh/ | Refresh Player Gac Bracket |
GET | /players/{player_id}/gac/current/ | Get Player Gac Current |
GET | /players/{player_id}/gac/opponents/ | Get Player Gac Opponents |
GET | /players/{player_id}/gac/seasons-with-brackets/ | Get Player Gac Seasons With Brackets |
GET | /players/{player_id}/gac/seasons/ | Get Player Gac Seasons |
GET | /players/{player_id}/gac/seasons/{season_id}/timeline/ | Get Player Gac Season Timeline |
GET | /players/{player_id}/gac/timeline/ | Get Player Gac Timeline |
GET | /players/{player_id}/history/ | Get Player History |
GET | /players/{player_id}/mods/summary/ | Get Player Mods Summary |
GET | /players/{player_id}/team-readiness/ | Player Team Readiness |
GET | /players/{player_id}/titles/ | Get Player Titles Route |
GET | /players/{player_id}/units/ | Get Player Units |
GET | /players/{player_id}/units/{base_id}/mods/ | Get Player Unit Mods |
GET | /players/{player_id}/units/{unit_id}/ | Get Player Unit |
GET | /players/{player_id}/unlock-requirements/ | Player Unlock Requirements Bulk |
GET | /players/{player_id}/unlock-requirements/{base_id}/ | Player Unlock Requirements |
GET | /players/{player_id}/unlock-requirements/{base_id}/materials/ | Player Unlock Materials |
GET | /players/compare/{left_id}/{right_id}/titles/ | Diff Player Titles Route |
GET | /players/guild/{guild_id}/ | Get Players From Guild |
GET | /players/history/counts/ | Get Player History Counts |
GET | /raids/ | List Raids |
GET | /raids/{raid_id}/ | Get Raid |
GET | /skills/ | List Skills |
GET | /skills/{skill_id}/ | Get Skill |
GET | /task-history/ | List Task History |
GET | /task-history/{run_id}/ | Get Task Run |
POST | /tasks/backfill/gac-direction | Backfill Gac Direction |
POST | /tasks/backfill/guild-events | Backfill Guild Events |
POST | /tasks/backfill/localization | Backfill Localization |
POST | /tasks/backfill/meta-created | Backfill Meta Created |
POST | /tasks/scan/guilds | Scan New Guilds |
POST | /tasks/update/events | Update Events |
POST | /tasks/update/game-data | Update Game Data |
POST | /tasks/update/guilds | Update Guilds |
POST | /tasks/update/players | Update Players |
GET | /teams/ | List Teams |
POST | /teams/ | Create Team |
DELETE | /teams/{team_id}/ | Delete Team |
GET | /teams/{team_id}/ | Get Team |
PATCH | /teams/{team_id}/ | Update Team |
GET | /teams/base-ids/ | List Team Base Ids |
GET | /territory/battles/ | List Battles |
GET | /territory/battles/{battle_id}/ | Get Battle |
GET | /units/{unit_id}/ | Get Unit |
GET | /units/{unit_id}/materials/ | Unit Materials |
swgoh
Search Guilds
GET
/guilds/search/
Search Comlink for guilds whose name matches name (substring).
Read-only against Comlink — does not persist results into db.guilds.
Public; the actual "add this guild to our store" step is the
drill-in (GET /guilds/{guild_id}/) which upserts on cache miss
and is officer-gated. Non-officers searching can browse results and
drill into already-tracked guilds; untracked guilds 404 for them.
The minimum query length is 2 characters; shorter queries are rejected
with a 400 to avoid hammering Comlink with overly-broad searches.
Parameters
Query Parameters
name*
Type
Requiredstring
start_index
Type
integer
Default
0count
Type
integer
Default
20Responses
Successful Response
application/json
JSON { "additionalProperties": "string" }
[
]
Get History Counts
GET
/guilds/history/counts/
Return {guild_id: snapshot_count} across db.guilds_history.
Indexed $group aggregation — finishes in milliseconds even as
the history collection grows. Frontend-friendly dict shape so
callers can do counts[guild_id] directly.
Responses
Successful Response
application/json
JSON "additionalProperties": 0
{
}
Get Guild History
Get Guild
GET
/guilds/{guild_id}/
Resolve a guild by id.
Discovery (cache miss → Comlink) requires role officer or
higher — that's the "add a new guild" gate. Refreshing an
existing guild is open to everyone, whether implicit (the 24h
staleness auto-refresh) or explicit (?refresh=true): Comlink
call frequency per guild stays bounded either way and a fresh
view is the right page-load behavior for any viewer.
Side effect on first-time discovery: schedules a background
refresh of every member's roster. Without it, derived views
(unit ownership, GL counts, team readiness) would show zero
until the daily cron runs. The task is wrapped via
run_and_record so admins can track progress + errors.
Parameters
Path Parameters
guild_id*
Type
Requiredstring
Query Parameters
refresh
Type
boolean
Default
falseResponses
Successful Response
application/json
JSON
[
]
Refresh Guild Players
POST
/guilds/{guild_id}/refresh-players/
Manually trigger a refresh of every member's roster.
Officer-gated. Returns 202 + a brief acknowledgement; the
actual work runs as a background task (per-member refreshes
take a few seconds each, so a serial sweep across a 50-member
guild is multi-minute — too long to hold the request).
The data layer's max_age_seconds skip threshold (default
6 h) prevents thrashing when officers click the button
repeatedly. Track progress in /admin/tasks.
Parameters
Path Parameters
guild_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Get Guilds
Get Guild Mods Summary
GET
/guilds/{guild_id}/mods/summary/
Guild-wide mod headline: ML+15 totals, speed-mod totals, per-member rows.
Aggregates every member's parsed mods into a single rollup
suitable for the GuildDetail / GuildStatistics surfaces. Each
member row carries their total_speed / ml15_count / gold_count /
speed_arrow_count so the UI can render a leaderboard inline.
Public — same as the other guild rollups.
Parameters
Path Parameters
guild_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Get Guild Scoreboard
GET
/guilds/{guild_id}/scoreboard/
Per-member officer triage scoreboard (Theme E).
Combined ranked roster for "who's pulling weight" / "who needs
coaching" / "who would we recruit out". Returns:
members— per-member row with GP, league/division/skill,
activity, roster depth, mod-farming progressmediansandpercentiles(p25/p75) for each ranked metriccoaching_highlights— pre-sorted list of (member, metric)
pairs below the guild's p25, capped at 30 for readability
Officer-gated — the audit framed this surface as "officer
triage". Per-member raw data is otherwise available via the
individual player pages; the value-add here is the cross-member
ranking + coaching pass.
Parameters
Path Parameters
guild_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Get Guild Territory Battle
GET
/guilds/{guild_id}/territory-battle/
Per-member breakdown of the guild's most recent Territory Battle.
Read straight from the cached guild envelope — Comlink ships
recentTerritoryBattleResult on every include_activity=True
fetch, so this costs one Mongo read and no upstream call.
Open like the rest of the guild read surface: it is aggregate
participation over data the game shows every member of that guild
anyway, and it carries no roster detail the guild page doesn't.
null result when the guild has no finished TB in its cached
envelope, which is normal between battles rather than an error —
hence 200 with an empty body rather than a 404.
The scoring rule is deliberately narrow; see
:mod:app.swgoh.guild_territory_battle for why summing every
playerStat.score inflates the total ~5x AND reorders the
leaderboard.
Parameters
Path Parameters
guild_id*
Type
Requiredstring
Query Parameters
instance_id
Responses
Successful Response
application/json
JSON
[
]
Get Guild Territory Wars
GET
/guilds/{guild_id}/territory-wars/
Every Territory War we have ever captured for this guild.
Comlink only ever shows the last eight; this is the archive built from
every observation since capture began, so it grows past that window
instead of rolling over.
Parameters
Path Parameters
guild_id*
Type
Requiredstring
Query Parameters
limit
Type
integer
Default
100Responses
Successful Response
application/json
JSON
[
]
Get Guild Activity
GET
/guilds/{guild_id}/engagement/activity/
Activity heatmap + AFK roll-up for a guild.
Reads member[].lastActivityTime from the cached guild doc —
no new Comlink calls, no schema-specific parsing (the field has
been there since the project's first ingest). Returns per-member
rows sorted newest-active first, bucket counts (today / 1-3d /
4-7d / 8-14d / 15-29d / 30+d), and a top-level afk_count
using a 7-day threshold.
Public — same gating as the other guild rollups. The info is no
more sensitive than what the per-player lastActivityTime
already exposes individually.
Parameters
Path Parameters
guild_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Get Guild Contributions
GET
/guilds/{guild_id}/engagement/contributions/
Raid + daily-activity + ticket contribution rollup.
Derived from each member's memberContribution[] array (now
preserved after the Theme C un-pop). The parser classifies each
entry into a normalised bucket (raid / territory / daily_activity
/ guild_currency / ticket / challenge / other) via keyword
matching on the contribution type id — see
:data:app.swgoh.guild_engagement._BUCKET_KEYWORDS for the
rules.
Returns totals per bucket, the per-member parsed rows, plus
pre-sorted raid_leaderboard / ticket_leaderboard /
territory_leaderboard slices (top 50 each) so the UI can
render leaderboard tables without re-sorting.
Public — same policy as the activity endpoint.
Parameters
Path Parameters
guild_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Get Guild Speed Sweep
GET
/guilds/{guild_id}/speed-sweep/
Every member's speed on one specific unit, sorted desc.
Requires login (any role) per the design decision — speed-sweep
is an arena-scouting tool useful enough that anonymous users can
pick it up off a Google scrape; logged-in friction is the right
compromise. Per-player mod data is otherwise public, so the gate
is UX-level, not a hard privacy line.
Returns rows sorted by total_speed desc. Members missing the
unit drop out (the question is "who has the fastest X", not "who
needs to farm X").
Parameters
Path Parameters
guild_id*
Type
Requiredstring
Query Parameters
base_id*
Type
Requiredstring
min_speed
Type
integer
Default
0Responses
Successful Response
application/json
JSON
[
]
Get Players From Guild
Get Player History Counts
GET
/players/history/counts/
Return {player_id: snapshot_count} across db.players_history.
Indexed $group aggregation — bounded by index size, finishes in
milliseconds even as the history collection grows. Frontend-friendly
dict shape so the player picker can do counts[player_id] lookups.
Responses
Successful Response
application/json
JSON "additionalProperties": 0
{
}
Get Player History
GET
/players/{player_id}/history/
Return per-player slim headline snapshots, newest first.
See :meth:Players.get_player_history for the schema and write
semantics. Sync handler — only Mongo I/O, but matches the broader
pattern of using sync routes for blocking work so anyio can
thread-pool the call.
Parameters
Path Parameters
player_id*
Type
Requiredstring
Query Parameters
skip
Type
integer
Default
0limit
Type
integer
Default
100Responses
Successful Response
application/json
JSON
[
]
Get Player Activity
GET
/players/{player_id}/activity/
Return narrative events for a player, newest first.
types is an optional CSV filter (e.g. unit_geared,unit_relic_promoted)
— unknown types are dropped silently rather than rejected. Empty
or missing types returns all event types.
See :meth:Players.get_player_activity for the row shape.
Parameters
Path Parameters
player_id*
Type
Requiredstring
Query Parameters
skip
Type
integer
Default
0limit
Type
integer
Default
100types
Responses
Successful Response
application/json
JSON
[
]
Get Player Gac Seasons
GET
/players/{player_id}/gac/seasons/
Per-season GAC summaries for the Season History card.
Public — same gating as /history/ and /activity/. Returns
a paginated list of {season_id, league_id, division_id, rank, points, wins, losses, join_time, end_time}. Sorted newest-first
by join_time so the most recent season is row zero.
Empty list if the player isn't tracked yet, or hasn't been
refreshed since the Phase 1 ingest helpers landed — the upsert
runs on the next get_player call, so a quick refresh fills
the collection.
Parameters
Path Parameters
player_id*
Type
Requiredstring
Query Parameters
skip
Type
integer
Default
0limit
Type
integer
Default
30Responses
Successful Response
application/json
JSON
[
]
Get Player Gac Current
GET
/players/{player_id}/gac/current/
Live snapshot of the player's current GAC season.
Read straight from the cached db.players envelope — no
Comlink call. Useful for the PlayerGac header (league/division/
skill-rating pill, current W-L, points) without a separate
request to the full player endpoint. 404 if the player isn't
tracked at all.
Parameters
Path Parameters
player_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Get Player Gac Bracket
GET
/players/{player_id}/gac/bracket/
Latest captured GAC bracket for a player, plus per-row diffs.
Reads the most recent db.gac_brackets snapshot and diffs it
against the previous same-season snapshot — each row in the
returned players array carries points_delta and
rank_delta (positive rank_delta = climbed; smaller rank
numbers are better).
Public — same gating as /gac/seasons/ and /gac/current/.
The bracket itself doesn't expose anything more sensitive than
the per-player headline (opponent names + bracket points), and
the cadence is already bounded by the scheduled capture's TTL.
Returns 404 when no bracket has ever been captured for this
player. That's distinct from "captured but empty" — an empty
players array would surface on a snapshot CG returned with
no entries (rare, mid-season-end edge case).
Parameters
Path Parameters
player_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Refresh Player Gac Bracket
POST
/players/{player_id}/gac/bracket/refresh/
Manually capture this player's current GAC bracket.
Officer-gated. Honors the per-player 5h TTL inside
:meth:Gac.capture_player_bracket — clicking twice in quick
succession is a safe no-op. Wrapped via run_and_record so
the click shows up in the admin task-history view.
Returns the capture result envelope (status / season_id /
observed_at / reason) verbatim so the UI can decide whether to
show "captured" toast vs "TTL — try again later" hint.
Parameters
Path Parameters
player_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Get Player Gac Timeline
GET
/players/{player_id}/gac/timeline/
Most-recent-season snapshot timeline.
Convenience shortcut for the default PlayerGac view — saves a
round-trip discovering the latest season id. Returns the same
envelope as the explicit /timeline/{season_id}/ route.
404 when the player has zero bracket captures (i.e. Phase 2 hasn't
ingested anything for them yet).
Parameters
Path Parameters
player_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Get Player Gac Season Timeline
GET
/players/{player_id}/gac/seasons/{season_id}/timeline/
Snapshot timeline for one specific captured season.
Drives the per-season tab in the PlayerGac timeline UI. Returns
every snapshot for the (player, season) pair, each carrying the
self deltas + the bracket "top gainer" for the interval ending
at that snapshot.
404 if the (player, season) pair has no captures.
Parameters
Path Parameters
player_id*
Type
Requiredstring
season_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Get Player Gac Seasons With Brackets
GET
/players/{player_id}/gac/seasons-with-brackets/
Distinct seasons we have at least one bracket snapshot for.
Powers the season picker in the timeline UI. Returns a flat list
of season_id strings — newest-first ordering is left to the
client because season_id has no canonical lex order (CG embeds
the season number inside a longer template id).
Parameters
Path Parameters
player_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Get Player Gac Opponents
GET
/players/{player_id}/gac/opponents/
Head-to-head roll-up — recurring bracket-mates across all seasons.
Each row carries appearances (total snapshots this opponent
has appeared in our brackets), season_count /
most_recent_season, and the opponent's most-recently-observed
name + guild_name so renames / guild transfers don't
desync the H2H card.
Frequency-only — we don't claim wins/losses here because per-round
opponent identification isn't reliable yet (see the Phase 3
scope notes in app/swgoh/gac.py).
Parameters
Path Parameters
player_id*
Type
Requiredstring
Query Parameters
limit
Type
integer
Default
30Responses
Successful Response
application/json
JSON
[
]
Get Player Units
Get Player Titles Route
GET
/players/{player_id}/titles/
Player title achievements (Theme G).
Resolves the cached unlockedPlayerTitle[] + selectedPlayerTitle
fields against the title catalog (db.player_title). Returns
the full unlocked list with friendly names + descriptions, plus
the currently-displayed title for the player.
Public — same gating as the other per-player reads. No Comlink
call; pure derivation over the cached envelope.
Parameters
Path Parameters
player_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Diff Player Titles Route
GET
/players/compare/{left_id}/{right_id}/titles/
Title comparison between two players for PlayerCompare.
Returns left-only / right-only / shared lists (each with friendly
title names) plus per-side and shared counts. Useful as a
"completionism" diff — see exactly what titles one player has
that the other doesn't.
Parameters
Path Parameters
left_id*
Type
Requiredstring
right_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Get Player Arena Defense
GET
/players/{player_id}/arena-defense/
Squad + fleet arena defensive lineups for a player.
Returns {squad: {rank, lineup}, fleet: {rank, lineup}} where
lineup is an ordered list of base_ids (leader first for squad,
capital ship first for fleet). Empty lineup arrays mean the
player hasn't placed a defense or the cached envelope predates
the field — UI renders that as "no defense placed".
Read straight from the cached db.players envelope — no Comlink
call. Public, same gating as the rest of the per-player reads.
Parameters
Path Parameters
player_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Get Player Unit
Get Player Mods Summary
GET
/players/{player_id}/mods/summary/
Whole-roster mod analytics: headline counts + top fastest + breakdowns.
Walks every unit envelope in db.units for this player, parses
each unit's equippedStatMod[] against the db.stat_mods
template cache, and rolls up to:
- headline — speed_mod_count, gold_count, ml15_count, six_pip_count,
speed_arrow_count, total_roster_speed, fastest_unit - all_units — full sortable list of every modded unit, with
per-unit total_speed / ml15_count / has_speed_arrow etc. - slot_distribution — per-slot count + avg speed
- set_distribution — per-set equipped count
Public — same gating as the rest of the per-player read paths. No
new Comlink calls; all derivation is over the cached unit
envelopes we already store.
Parameters
Path Parameters
player_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Get Player Unit Mods
GET
/players/{player_id}/units/{base_id}/mods/
Per-unit mod detail: the 6 mods + their per-mod quality / speed.
Used by the per-unit drawer in PlayerMods and as the data source
for an inline mod display on UnitDetail. Returns the parsed mods
- a per-unit summary so callers don't have to re-compute.
404 if the player doesn't own the unit. Returns an empty mods
list (not a 404) when they own it but haven't equipped any —
that's "they own a freshly-unlocked unit", not an error state.
Parameters
Path Parameters
player_id*
Type
Requiredstring
base_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]
Get Player
GET
/players/{code}/
Resolve a player by ally code or player_id.
Discovery (cache miss → Comlink) requires role officer or
higher — that's the "add a new player" gate. Refreshing an
existing player is open to everyone, whether implicit (the
~28h staleness auto-refresh) or explicit (?refresh=true):
Comlink call frequency per player stays bounded either way and
a fresh view is the right page-load behavior for any viewer.
Parameters
Path Parameters
code*
Type
Requiredstring
Query Parameters
refresh
Type
boolean
Default
falseResponses
Successful Response
application/json
JSON
[
]
Get Players
GET
/players/
Return a slim projection of every tracked player.
Intentionally omits roster, stats, and profileStat[] — only the fields
a list view needs are projected server-side. See
:meth:Players.get_players for the projection details.
Responses
Successful Response
application/json
JSON { "additionalProperties": "string" }
[
]
Update Game Data
POST
/tasks/update/game-data
Refresh game data from Comlink into MongoDB.
Optional segment (0–4) restricts the refresh to a single
getGameData segment. Segment 0 contains keys that aren't returned
by 1–4 (notably unitGuideDefinition). Optional key further
restricts it to a single Comlink type within that segment
(e.g. key=units).
refresh_loc (default True) folds the localization bundle fetch
into the same run so each item's data.localized.* is stamped at
write time. Pass refresh_loc=false when you only want to refresh a
specific segment without re-fetching the (large) localization payload.
Returns 502 with the upstream message if Comlink is unhealthy (typically
an APK-version mismatch on the Comlink server itself).
Authorizations
APIKeyQuery
Type
API Key (query: api-key)
or
APIKeyHeader
Type
API Key (header: x-api-key)
Parameters
Query Parameters
segment
key
refresh_loc
Type
boolean
Default
trueResponses
Successful Response
application/json
JSON
[
]
Backfill Meta Created
POST
/tasks/backfill/meta-created
One-shot: stamp meta.created on legacy docs missing it.
Approximates meta.created / meta.created_timestamp from the
existing meta.updated / meta.timestamp values on docs where
meta.created doesn't yet exist. Idempotent — re-running after the
first successful run is a no-op. Optional type_ query param scopes
the backfill to one game-data collection (e.g. ?type_=characters);
omitted, runs across every game-data collection.
Authorizations
APIKeyQuery
Type
API Key (query: api-key)
or
APIKeyHeader
Type
API Key (header: x-api-key)
Parameters
Query Parameters
type_
Responses
Successful Response
application/json
JSON
[
]
Update Guilds
POST
/tasks/update/guilds
Force-refresh every guild known to db.guilds from Comlink.
Iterates every doc in db.guilds and calls
:meth:Guilds.get_guild with force=True per guild_id, bypassing
the staleness gate. One guild's failure doesn't abort the run.
Returns per-guild outcome counts plus a capped error list.
This is the same path the scheduled refresh_guilds job runs —
handy for manual triggers or verifying the bulk path before the
schedule fires.
Authorizations
APIKeyQuery
Type
API Key (query: api-key)
or
APIKeyHeader
Type
API Key (header: x-api-key)
Responses
Successful Response
application/json
JSON
[
]
Update Players
POST
/tasks/update/players
Force-refresh every player known via any guild in db.guilds.
Walks every guild doc, collects unique guild.member[].playerId
values, then calls :meth:Players.get_player with refresh=True
per id. One player's failure doesn't abort the run. Returns per-player
outcome counts plus a capped error list.
This is the same path the scheduled refresh_players job runs.
Players that exist in db.players but aren't currently in any
tracked guild are not refreshed by this endpoint — by design.
Authorizations
APIKeyQuery
Type
API Key (query: api-key)
or
APIKeyHeader
Type
API Key (header: x-api-key)
Responses
Successful Response
application/json
JSON
[
]
Scan New Guilds
POST
/tasks/scan/guilds
Discover guilds we don't track yet by scanning db.players.
Walks every player record, finds player.guildId values we don't
already have in db.guilds, fetches each via
:meth:Guilds.get_guild, and pulls any newly seen guild's members
into db.players.
Same path the scheduled scan_new_guilds job runs. Returns
{candidates, new_guilds, added_players, errors}.
Authorizations
APIKeyQuery
Type
API Key (query: api-key)
or
APIKeyHeader
Type
API Key (header: x-api-key)
Responses
Successful Response
application/json
JSON
[
]
Update Events
POST
/tasks/update/events
Refresh live events from Comlink /getEvents into db.events.
Each event is flattened into one Mongo doc per (event, instance)
pair so time-window queries (active / upcoming / past) hit
indexed range filters directly.
refresh_loc (default True) also refreshes the localization
bundle so event.localized.* is stamped against the latest
translations. Pass false to use whatever's currently in
db.localization and skip the loc fetch.
Authorizations
APIKeyQuery
Type
API Key (query: api-key)
or
APIKeyHeader
Type
API Key (header: x-api-key)
Parameters
Query Parameters
refresh_loc
Type
boolean
Default
trueResponses
Successful Response
application/json
JSON
[
]
Backfill Guild Events
POST
/tasks/backfill/guild-events
One-shot: recover finished TB / TW results from stored snapshots.
Comlink's guild envelope only carries the last one Territory Battle
and the last eight wars, but Guilds._save_history has been
snapshotting whole envelopes for as long as guilds have been tracked —
so older events are still on disk, buried in ~648 KB rows nothing read.
This walks them into db.guild_tb_results / db.guild_tw_results.
Nothing is invented: a row appears only if some snapshot actually
observed that event. Idempotent, so re-running reports zeros rather
than duplicating, and it is safe to run while the daily refresh is
going.
Authorizations
APIKeyQuery
Type
API Key (query: api-key)
or
APIKeyHeader
Type
API Key (header: x-api-key)
Parameters
Query Parameters
guild_id
Responses
Successful Response
application/json
JSON
[
]
Backfill Gac Direction
POST
/tasks/backfill/gac-direction
One-shot: repair GAC promotion/demotion events stored inverted.
Division 5 is the top of a league, not Division 1, so every
gac_promoted / gac_demoted row written before that was fixed
says the opposite of what happened.
Dry run by default — this rewrites history, so apply=false
returns the counts without touching anything. Pass apply=true to
write.
The direction is recomputed from each row's own details rather than
the type being flipped: a league change was already correct (the league
dominates), and a blind flip would corrupt those. That also makes it
idempotent.
Authorizations
APIKeyQuery
Type
API Key (query: api-key)
or
APIKeyHeader
Type
API Key (header: x-api-key)
Parameters
Query Parameters
apply
Type
boolean
Default
falseplayer_id
Responses
Successful Response
application/json
JSON
[
]
Backfill Localization
POST
/tasks/backfill/localization
One-shot: stamp data.localized.* on existing game-data docs.
Refreshes the localization bundle and walks every game-data collection
(or just type_), resolving data.{nameKey,descKey,descriptionKey}
into data.localized.{name,desc,description} for each doc that has
at least one resolvable key. Idempotent — re-running rewrites the same
fields from scratch, no duplication.
Authorizations
APIKeyQuery
Type
API Key (query: api-key)
or
APIKeyHeader
Type
API Key (header: x-api-key)
Parameters
Query Parameters
type_
Responses
Successful Response
application/json
JSON
[
]
Get Unit
Unit Materials
GET
/units/{unit_id}/materials/
Step-by-step gear + relic cost to take this unit to max.
One entry per remaining upgrade — finishing the gear ladder, then
each outstanding relic tier — plus a combined total. Costs are
baselined from what the unit has now, including partially-filled
gear slots.
Totals are gross: Comlink exposes no player inventory, so this is
what the unit requires, not what's left after what the player
already holds.
Parameters
Path Parameters
unit_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
List Characters
GET
/characters/
List characters.
By default returns one row per playable unit — canonical=true
collapses the raw Comlink rows (which emit one row per rarity tier and
several non-playable variants like PVE_*, _GL_EVENT,
_GLE_INHERIT, _TRANSFORMED) down to the maxRarity row of each
playable unit. Pass canonical=false to see every Comlink row
unfiltered.
gl=true returns only Galactic Legends (data.legend == true from
Comlink), gl=false returns only non-GLs, omitting the param applies
no GL filter. Combines with canonical: ?gl=true alone gives
every GL variant, ?gl=true&canonical=true (the default) gives one
row per playable GL.
ships=true returns only ships, ships=false returns only
characters (i.e. non-ships), omitting the param returns both. Comlink
lumps both unit kinds into the same units segment, hence both end
up in this collection; the filter keys off data.combatType
(string "CHARACTER" or "SHIP").
Parameters
Query Parameters
skip
Type
integer
Default
0limit
Type
integer
Default
200name
gl
ships
canonical
Type
boolean
Default
trueResponses
Successful Response
application/json
JSON { "additionalProperties": "string" }
[
]
Character Names
GET
/characters/names/
{baseId: display name} for every playable character.
A slim companion to GET /characters/ for the common case of
"turn a base_id into a name". The full catalog is ~16 MB because
each record carries its whole gear/skill/stat tree; this is ~20 KB.
Use it for any name lookup, and reserve the full endpoint for
callers that actually read the other fields (the unit catalog and
picker filter on combatType / obtainable, for example).
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Get Character
List Gear
Get Gear
List Types
List Items
Get Item
List Recommendations
Get Recommendation
List Abilities
Get Ability
List Skills
Get Skill
List Tokens
Get Token
List Conquests
Get Conquest
List Raids
Get Raid
List Battles
Get Battle
List Events
GET
/events/
List event instances.
status filters by current time-window: active (running now),
upcoming (starts later), past (already ended). Anything else
(including omitting it) returns the whole collection. type filters
on Comlink's readable enum string for event.type — e.g.
DOUBLE_DROP, CONQUEST, SHIP_EVENT — pass the value as-is.
Sorted most-recent-first by start_time_ms.
Parameters
Query Parameters
status
type
skip
Type
integer
Default
0limit
Type
integer
Default
200Responses
Successful Response
application/json
JSON { "additionalProperties": "string" }
[
]
Get Event
GET
/events/{event_id}/
Return the most-recent instance for event_id.
Many events recur (e.g. shipevent_PRELUDE_ACKBAR runs continuously);
this endpoint surfaces the latest occurrence as a single doc. To get a
specific historical instance use the /{event_id}/{instance_id}/ form
or the list endpoint with appropriate filters.
Parameters
Path Parameters
event_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Get Event Instance
List Task History
GET
/task-history/
List task-run summaries, most-recent first.
Optional filters:
task_name— exact match (e.g.refresh_guilds,
refresh_game_data,scan_new_guilds, …).status—successorfailed.trigger—scheduledormanual.
limit is clamped to 1000 to keep responses bounded.
Parameters
Query Parameters
task_name
status
trigger
skip
Type
integer
Default
0limit
Type
integer
Default
100Responses
Successful Response
application/json
JSON { "additionalProperties": "string" }
[
]
Get Task Run
List Team Base Ids
GET
/teams/base-ids/
Flat deduplicated list of every base_id used by any team slot.
Drives the PlayerCompare "key units" head-to-head — units that
show up in at least one defined team are GAC-relevant by
definition (officers curate by editing teams). Sorted
alphabetically so the response is stable across calls.
Public — same policy as the rest of the team-list reads.
Responses
Successful Response
application/json
JSON "string"
[
]
List Teams
GET
/teams/
List all teams, optionally filtered. Newest first.
Public — same policy as the team-readiness rollups that read
from this list. Create / update / delete remain officer-gated.
Parameters
Query Parameters
category
combat_type
Responses
Successful Response
application/json
JSON { "additionalProperties": "string" }
[
]
Create Team
POST
/teams/
Parameters
Request Body
application/json
JSON "name": "string", "description": "string", "categories": [ "string" ], "combat_type": "character", "lineup_size": 5, "members": [ { "base_id": "string", "required": true, "min_relic": 0, "min_zetas": 0, "min_omicrons": 0, "required_skills": [ { "skill_id": "string", "kind": "string" } ] } ]
{
}
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Get Team
GET
/teams/{team_id}/
Single-team detail. Public — the list endpoint already returns
the same public_team shape for every team, so gating just one
of them would be inconsistent. Edit / delete remain officer-gated.
Parameters
Path Parameters
team_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Delete Team
Update Team
PATCH
/teams/{team_id}/
Partial update.
Only fields explicitly present in the request body are touched.
Required-non-null fields (name / combat_type / members)
silently drop an explicit null rather than corrupting the doc;
nullable fields (description / category) honor the clear.
Parameters
Path Parameters
team_id*
Type
Requiredstring
Request Body
application/json
JSON "name": "string", "description": "string", "categories": [ "string" ], "combat_type": "string", "lineup_size": 0, "members": [ { "base_id": "string", "required": true, "min_relic": 0, "min_zetas": 0, "min_omicrons": 0, "required_skills": [ { "skill_id": "string", "kind": "string" } ] } ]
{
}
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Guild Team Readiness Summary
GET
/guilds/{guild_id}/team-readiness/
Per-team bucket distributions across the whole guild.
One row per team. The frontend either renders the granular
buckets directly (twelve-bar histogram) or rolls them up into
the four-headline summary for the overview list.
Public — same policy as /guilds/{id}/unlock-readiness/. Team
creation/edit/delete remains officer-gated; the list is just a
derived rollup over public roster data.
Parameters
Path Parameters
guild_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Guild Team Readiness Detail
GET
/guilds/{guild_id}/team-readiness/{team_id}/
Per-member breakdown of a single team across the guild.
Returns one entry per guild member with that player's tier, the
weakest-link descriptor (when one exists), and a parallel-to-the-
team-roster array of slim per-slot details so the detail table
can colour each cell by individual unit tier.
Parameters
Path Parameters
guild_id*
Type
Requiredstring
team_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Player Team Readiness
GET
/players/{player_id}/team-readiness/
Every defined team scored against one player's roster.
Sorted server-side by best-tier-first so the player (or anyone
looking at them) sees "what they're ready for" before "what
they're missing". Ties broken by team name for stability.
Public — same policy as /players/{id}/unlock-requirements/;
surfacing one player's readiness against the team list doesn't
expose anything an officer-only roster view would have hidden.
Parameters
Path Parameters
player_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Guild Unit Ownership
GET
/guilds/{guild_id}/unit-ownership/{base_id}/
Per-character ownership snapshot across one guild.
Walks each member's unit doc for base_id and buckets them on
the same granular tier ladder team-readiness uses (missing /
unlocked / g13 / r1 … r9). Returns the headline
ownership numbers + the bucket histogram + the coarse 4-headline
summary the frontend uses to render the "Your guild" card on
CharacterDetail.
Public — same policy as the per-character unlock endpoints. The
data is derived from public roster info; we don't expose
anything /players/{id}/ doesn't already.
Parameters
Path Parameters
guild_id*
Type
Requiredstring
base_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Character Unlock Requirements
GET
/characters/{base_id}/unlock-requirements/
Catalog view: what does it take to unlock this unit?
404 when the unit has no guide entry at all (most non-event
characters — they're farmable, no unlock requirements). Returns
has_data=False for units that have a guide entry but where
we couldn't resolve a per-character requirement list (older
Legendary events that gate by category rather than named units).
Parameters
Path Parameters
base_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Player Unlock Requirements Bulk
GET
/players/{player_id}/unlock-requirements/
Bulk: every gated character scored against the player's roster.
With ?gl=true the response is restricted to Galactic Legends
(the highest-interest unlocks); with ?gl=false to non-GLs;
omitted returns both. Default is "everything we have a guide for"
so the front-end can decide what to filter client-side.
Public — same as the per-character variant. Officers don't get
additional info here.
Parameters
Path Parameters
player_id*
Type
Requiredstring
Query Parameters
gl
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Player Unlock Requirements
GET
/players/{player_id}/unlock-requirements/{base_id}/
Player-scored view: catalog + this player's progress overlay.
Same 404 semantics as the catalog endpoint. When the player owns
no units at all (unknown player_id) the response still returns
has_data from the catalog and per-task met=False rather
than 404 — there's nothing player-specific that's missing, just
everything-is-zero.
Parameters
Path Parameters
player_id*
Type
Requiredstring
base_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Player Unlock Materials
GET
/players/{player_id}/unlock-requirements/{base_id}/materials/
Total gear + relic materials still needed for this unlock.
Sums, across every required character the player hasn't yet brought
to its threshold, the gear salvage / relic materials / credits
needed to close the gap — baselined from what each character has
now, including partially-filled gear slots.
Kept separate from the scored endpoint above because it walks the
recipe tree across three collections; that endpoint is consumed by
several list views and shouldn't pay this cost. Fetch this one
lazily when the panel is opened.
Note the totals are gross: Comlink exposes no player inventory, so
this is what the characters require, not what's left after what the
player already holds. The response says so in caveats.
Parameters
Path Parameters
player_id*
Type
Requiredstring
base_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Guild Unlock Readiness Overview
GET
/guilds/{guild_id}/unlock-readiness/
Public guild overview: every gated character with guild-wide aggregates.
For each unlockable character (filterable to GLs / non-GLs), counts
members in five buckets: already_unlocked, ready (all prereqs met
but not unlocked), near (within 2 prereqs), in_progress (any
progress), locked_away (no progress at all). Plus the top-3
closest members per character so the row preview tells the leader
"push these three" without a click.
Sort: members_near_unlock desc, then members_unlocked desc,
then alphabetical. Surfaces "where farm pushes pay off most" first.
Parameters
Path Parameters
guild_id*
Type
Requiredstring
Query Parameters
gl
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Guild Unlock Readiness
GET
/guilds/{guild_id}/unlock-readiness/{base_id}/
Public guild rollup: every guild member ranked against one gated unit.
Useful for "who in the guild is closest to unlocking GL Luke?" and
for prioritising farm assignments. Each row carries the same per-
player summary the bulk player endpoint produces (overall_met,
overall_total, already_unlocked, phases). Members not in the
units collection at all are still listed with everything zeroed
so the leader sees "we don't have roster data for X" instead of
silently dropping them.
Parameters
Path Parameters
guild_id*
Type
Requiredstring
base_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON "additionalProperties": "string"
{
}
Get Leaderboard
GET
/leaderboards/
Ranked top-N tracked players by one headline metric.
metric must be one of the keys in
:data:app.swgoh.leaderboards.RANKED_METRICS; unrecognised
metric strings 400. limit is clamped to [1, 500].
guild_id (optional) scopes the ranking to a single guild's
current roster — the "see only one guild" filter. When the
guild isn't tracked the response carries guild_not_tracked: true with an empty rows so the UI can show a hint.
Each row carries the ranked metric value + a small set of side
metrics (GP / skill_rating / R7+ count / etc.) so the UI can
render context columns without re-fetching per player.
Parameters
Query Parameters
metric
Type
string
Default
"gp_total"limit
Type
integer
Default
100guild_id
Responses
Successful Response
application/json
JSON
[
]
List Metrics
GET
/leaderboards/metrics/
List the whitelisted ranked metrics.
Drives the metric picker on the leaderboards view so the frontend
doesn't keep a parallel table — adding a new metric is one entry
in the backend whitelist and the picker picks it up automatically.
Responses
Successful Response
application/json
JSON { "additionalProperties": "string" }
[
]
Recent Unlocks
GET
/leaderboards/recent-unlocks/
Cross-player unit_acquired event feed, newest first.
guild_id (optional) scopes the feed to that guild's current
members — pairs with the leaderboard's guild filter so both
panels move together. Capped at 500. Each row carries identity +
the unit's base_id + rarity / gear / relic_display at acquisition
time. The frontend renders these with localized unit names via
its existing catalog.
Parameters
Query Parameters
limit
Type
integer
Default
100guild_id
Responses
Successful Response
application/json
JSON
[
]
List Notifications
GET
/me/notifications/
Per-user notification feed.
Aggregates db.player_events for the logged-in user's own
player_id plus their followed players, filtered to the
notifiable event types. Returns recent events + total unread
count (events newer than notifications_seen_at).
Parameters
Query Parameters
limit
Type
integer
Default
50Responses
Successful Response
application/json
JSON
[
]
Mark Notifications Seen
List Follows
Follow Player
POST
/me/follows/{player_id}/
Add player_id to the user's followed list. Idempotent.
Capped at 50 follows per user — the cap is enforced server-side
in :func:app.swgoh.notifications.follow and silently drops
the 51st request to keep the panel snappy.
Parameters
Path Parameters
player_id*
Type
Requiredstring
Responses
Successful Response
application/json
JSON
[
]