Recording and coverage
What Elixir captures, how often, and why an answer is never a live scrape.
The official Clash Royale API answers only "what is true right now": a profile, a clan roster, and a battle log of roughly the last 30 battles (most commonly exactly 30). Elixir MCP records those observations continuously and keeps them. This page is what that promise means precisely.
The game's own "last seen"
Clash Royale reports lastSeen for each member inside a clan's member
list only — a player's own endpoint does not carry it. Elixir MCP captures
it on every clan roster poll and serves it as last_seen_in_game on
players_profile and on each clans_roster member.
Three things worth knowing:
- It is when the player was last active, not when this service last
looked, and not
last_recorded_battle— which only moves when a battle was captured. A member who opens the game daily without battling is indistinguishable from a departed one without it. - It is the predicate the game uses to seed a river race roster. Members
last seen before a race began are absent from that race's participants
entirely, whether or not they battle and whether or not they joined in
time. That is why
war_current.members_not_in_raceand this field belong together. - It cannot be backfilled, and it is only obtainable while a player is in a clan being polled. Null means no polled roster has carried them yet.
Added means recorded
There is no watch step and no approval queue. Tracked means recorded: tracking a subject on your account is the request to record it, and capture starts at the next scheduler tick. (Before contract 1.0.0 the tools said "add"; the act is the same.)
| Act | Tool | Web | Scope needed |
|---|---|---|---|
| Record a player | elixir_track_player({ player_tag, relationship? }) |
Account → Tracking | recordings:write |
| Record a clan | elixir_track_clan({ clan_tag, scope? }) |
Account → Overview | recordings:write |
| Stop | the same tools with action: "remove" |
same | recordings:write |
| Silence the feed without stopping | action: "notify_off" / "notify_on" |
same | recordings:write |
Slots are the only gate; see Limits.
One recording, many reasons
Every subject has at most one active recording, shared by everyone who wants it. The recorder counts the reasons a subject is wanted and starts, widens, or stops the recording accordingly:
| Reason | Source |
|---|---|
| claimed | any account tracks the player |
| added | any account tracks the clan |
| added deep | any account tracks the clan at comprehensive scope |
| collected | the subject is a member of a collection |
| collected deep | a member of a comprehensive collection |
| ranked | the player appeared in the recording top-N of a leaderboard this season; comprehensive, and sticky until the next season roll plus three days |
| ops | the maintainer records it directly; never stopped by user actions |
The widest reason wins. Removing your own reason frees your slot; the recording stops only when no reason remains anywhere. A clan's scope settles up or down to the widest remaining reason; a player's scope only ever widens. History is never deleted when a recording stops.
Scope: what is actually polled
| Subject | activity |
comprehensive |
|---|---|---|
| Player | profile only | profile and battle log |
| Clan | clan roster, current river race, river race log | the same, plus profile and battle log for every current member, following joins and leaves |
Put plainly: activity follows the clan itself — its roster, its members arriving and leaving, and its river races. Comprehensive also records every member's battles, which is what builds the clan a full history rather than a record of who was in it.
Every tier has one activity clan slot, and it is meant for your own clan: it is
what approval spends when your access request is granted, so the clan you play
in is being followed from your first sign-in. Comprehensive is the upgrade, and
it costs proportionally more to run — the member tier has none, so
elixir_track_clan, which defaults to comprehensive, needs
scope: "activity" there.
Players you track are always comprehensive. A recorded player's current clan is also read for roster and membership tracking — a few times a day, following the clan's own liveliness — without a slot and without polling the other members.
A roster poll records what it carries (since 2026-09-17). Every clan the
recorder reads gets one row per game day of its own numbers (clan score, war
trophies, member count, required trophies, weekly donations), and every member
on that roster gets a row in the same daily series a recorded player's profile
writes — trophies, donations, clan rank, arena and the game's own last-seen —
whether or not their profile is recorded. The two writers share the row: a
member with a recorded profile has the profile's lifetime counters on it too,
and each half is dated by its own observation. The profile's progress buckets
(the seasonal Trophy Road, 2v2 League, Merge Tactics) are kept as a series of
their own; a bucket the player has not touched this season writes nothing. The
readers over these series arrive with the next contract bump; today
players_timeline already returns the roster-written days.
Relationships, primary, nicknames
Each player you track is your primary (exactly one: the first you track,
or whichever you mark relationship: "primary"), an alt, a friend, or
someone you are watching (the default). The primary is what "omit
player_tag" means on your connection. Claims are taken at your word
(claim_status: unverified); several accounts may track the same player and
share the recording.
elixir_nickname({ player_tag, nickname }) stores a private label (1 to 40
characters, null clears) that only your account and your agents see;
players_search ranks your nicknames first. It is the one write the website's
Explore page performs.
Collections
A collection is a curated, named group (slug ^[a-z0-9][a-z0-9-]{1,38}$,
public or private, player or clan kind) that records its members:
adding a tag to a collection is a recording reason like any other, at the
collection's scope (comprehensive by default). collections_edit takes
add, remove or set with up to 500 tags per call and refuses the whole
call on one malformed tag. Collections are a family-tier feature; reading
public ones needs only cr:read.
Leaderboards
The CR API shows a ranking as it is this minute and forgets it. The recorder keeps it: the global Path of Legends board and every location the API lists — 262 countries and regions — once a day, in the first planning tick after 10:00Z, the hour the season rolls, so a season's last daily snapshot is the board as it stood going into the roll. Each fetch that differs from the last becomes a snapshot with a row per placed player (rank, rating, name, clan); an identical later fetch confirms the existing snapshot rather than duplicating it, so the record also says how long a board held.
rankings_players reads a board — the latest, or as it was at any earlier
instant with as_of — paged, because a whole board can run to a thousand
places. rankings_clans aggregates it: which clans have the most rated
players, ties broken by the clan's best-placed player. Both count over
everyone above the rating floor, not a top-100 slice; a Path of Legends
board lists only players above that floor, and a season resets everyone below
it, so a board is small in a season's first days and fills through the month.
live: true on either asks for a read of the board no older than a minute:
served if in hand, otherwise queued while the latest snapshot answers with
live_status.state: "pending".
Everything else the API forgets about a season is recorded beside it.
A season's final Path of Legends board — the settled standing at full
depth, 9,999 places — is fetched once the day after it rolls and was
backfilled for every season since October 2022, the ranked ladder's first;
read it with board: pol_final and a season, either the number
game_clock counts (135 for August 2026) or the API's own name for the
season, the month it started in (2026-08). The Pass's in-game "Season 87"
is a third numbering the API does not use anywhere. The clan ladders (clans by clan
score, clanwars by clan war trophies, 1,000 places by location) are
recorded daily for global, the United States and Japan — rankings_clan_ladder.
The game-mode leaderboards (Merge Tactics, Touchdown, 2v2 League and the
rest) are enumerated from the API daily, so a board that rotates in is
followed without anyone naming it. Call
rankings_players({ board: "mode", location: "list" }) for the recorded
ids, names and enabled state, then read board: "mode" with a returned
location. The catalog is a recorded read; omit live and as_of when
listing it. A mode board or badge is not evidence that its battles appear
in the API's battle log; missing recorded battles do not prove absence.
What was on — the events the API listed as running, with no dates — is
recorded daily as sightings, so game_events is the season's calendar built
from the days each event was seen. rankings_timeline reads any of the player
boards across a window: a player's rank and rating at every snapshot, a
clan's rated players and best rank, or the board's own floor, summit and
field size — the season story at daily resolution.
A top-200 appearance on the global board is a recording reason. Any player who reaches it is recorded at comprehensive scope — every battle, with the rank and rating each one carried — until the next season roll plus three days, however far they fall in between. That grace is deliberate: the board is empty for the first hours after a roll, and the only way the opening battles of the next season's #1 are captured is that they were recorded for being in last season's field. The Trophy Road boards are watched but have been served empty by the API for recent seasons.
How often a subject is fetched
Recording is a schedule over one shared, conservative API budget that the whole collector fleet stays inside. The rules below only ever shorten each other.
| Subject | Rule | Bounds |
|---|---|---|
| Battle log | poll when about five new battles are expected, from an exponentially weighted average of battles per hour actually harvested | 15 minutes to 24 hours; a new subject starts hourly |
| Battle log, burst bound | poll before half the fastest time this player has recently filled the log (busiest six-hour window of the last 14 days) | never above the yield rule |
| Battle log, reader cap | any player resolved by a tool call (yours, or one you named) is polled at least hourly for the next 24 hours | – |
| Battle log, floor | at least daily regardless | – |
| The roster gate (profile only) | a clan roster carries the game's own lastSeen for every member. When a tracked clan's roster is fresher than a member's profile poll and says they have not been in the game since it (and their last sighting is more than two hours old, so a session in progress never gates), the profile poll is skipped. An incidental clan's roster (read every 4 to 24 hours) never gates. Battle logs always follow their own yield, burst and reader rules: a completed session can fill the roughly 30-entry log before its lastSeen becomes visible, so it is not safe to use as a negative capture signal |
profile only; battle-log floors and bounds always apply |
| Profile | every 8 hours once a roster fresher than the last poll shows the player active; without roster information, every 8 hours for active players (0.5 battles/hour or more), daily for most, every 3 days when dormant; a player somebody tracks directly is capped at 8 hours; forced once in the hour before the Monday donation reset | no floor: an idle player owes the record no snapshot |
| Profile, arena request | a battle log names the arena each battle was played in, and that arena is the higher side's: a player just under a trophy gate is matched with people standing on it, and their own log shows the next arena for those battles. So only a Trophy Road battle the player entered with at least their opponent's trophies says where the player stood. When such a battle names an arena the latest snapshot does not carry, the profile is polled at the next planning tick, roster gate or not, and the arena move lands on the timeline within the battle log's cadence rather than the profile's | one outstanding request per player; served by the next profile admission |
| Clan roster, tracked | every 15 minutes while members are in the game (three or more seen this hour); hourly once nobody has been for an hour, unless the roster is churning (three or more joins, departures or promotions a day); every 4 hours once nobody has been seen for a day | floor 2 days |
| Clan roster, incidental | a clan read only because a recorded player is in it: every 4 hours while members are in the game, every 12 hours when idle, daily when nobody has been seen for a day. Their profile polls carry their clan tag, so membership history is never lost, only coarser | floor 2 days |
| Current river race | every 30 minutes on war days, every 2 hours on training days (the API names the day) | floor 2 hours |
| River race log | daily | floor 2 days |
| Card catalog | daily, one fetch for everyone | – |
Subjects added together are de-phased by a stable per-subject offset so a
batch does not poll in lockstep. A burst can still roll off the log between
two polls; the Status page publishes how many of the
last day's polls found that it had, and so does capture_audit_24h in
/api/public/status.
Freshness, as the envelope reports it
Every response's meta says what it was built from:
| Field | Meaning |
|---|---|
source_polls.<endpoint>.observed_at |
the last admitted poll of that source; null if never polled |
source_polls.<endpoint>.freshness_seconds |
age of that poll at as_of; null when observed_at is |
freshness_seconds |
the oldest relevant source age; null if any required source has never been polled |
recorded_since |
earliest stored history for the subject, which can predate any account adding it (imports, opponents' logs) |
recording_active_since |
when the current active recording began |
Freshness advances only when a payload is admitted, never on a fetch that was refused, so a stale value is honest.
Daily series
Three tables hold the record's day-grained history, one row per subject per game day, the last observation of the day winning:
| Series | Written by | From | Read by |
|---|---|---|---|
| a player's day (trophies, donations, arena, clan and rank, the game's own last-seen, and for a recorded profile the lifetime block: battles, wins, losses, three-crown wins, star points, collection level, king tower level, total donations, challenge and tournament counters, Path of Legends standings, seasonal trophies) | the roster poll of every clan the recorder follows (every member, whether or not their profile is recorded) and the profile poll of every recorded player, sharing one row | 2026-03-07 for the first recorded players; 2026-03-11 for POAP KINGS' members; the day a clan or player is first polled otherwise | players_timeline, clans_members_timeline |
| a clan's day (clan score, war trophies, member count, required trophies, weekly donations, type and location) | the roster poll | 2026-03-11 for POAP KINGS; the first poll otherwise | clans_timeline |
| a player's progress buckets (the seasonal Trophy Road, 2v2 League, Merge Tactics: trophies, best trophies, arena per bucket) | the profile poll | 2026-03-07 | players_timeline with progress_key |
Two writers share a member's row. The roster writes trophies, donations,
donations received, arena, the clan, the rank and the game's last-seen at
its own cadence (every fifteen minutes for a tracked clan); the profile writes
the lifetime block at its own (every eight hours or slower). Each writer's
columns are dated by its own stamp (roster_observed_at, profile_observed_at),
the shared columns belong to whichever observation is newer, and observed_at
is the newest of either. So "trophies on day D" is the day's last read from
either source; "wins on day D" is the day's last profile read, and a day the
roster wrote with no profile poll carries the roster's columns and null
elsewhere, with profile_observed_at null to say so.
source on a point is api for a row from a recorded payload (live or the
archive backfill of 2026-09-17) and elixir-bot for the few rows imported
from POAP KINGS' earlier bot on 2026-09-18: five member-only days before the
first archived roster (2026-03-07 to 03-11, their instant the bot's usual
roster hour) and the Sunday pre_reset rows for members whose profile was not
recorded then. A window that contains such a point says so in notes[]. The
July to September 2026 hole in the profile archive was filled by replaying
the bot's own profile payloads; that replay wrote rows and never moments, so
the timeline for those players is quiet over those weeks by rule, not by
absence of play.
kind selects the daily row (the default), the pre_reset row (the hour
before the Monday 00:10 UTC donation reset, the honest weekly donation total)
or the season_roll row (the hour before the season rolls). A bucket of the
progress series that reads zero trophies and zero best trophies is not a row:
no record for no activity.
Completeness
elixir_coverage({ player_tag? }) returns:
| Field | Meaning |
|---|---|
polls[] |
{ endpoint, last_admitted_at } for player and player_battlelog |
battles |
recorded_appearances, first_recorded, last_recorded, including appearances recorded from other players' logs before the tag was added |
snapshots.first_date |
first daily profile snapshot; timelines exist only from here |
observation_intervals[] |
consecutive profile snapshots bracket an interval; expected_battles is the lifetime battle counter's change, captured_battles counts recorded battles in (observed_from, observed_to]; is_complete and ratio are null with a note when the two are not comparable |
completeness_last_7_days |
average_ratio weighted by expected battles over intervals ending in the last 7 days; measured_intervals, unknown_intervals, incomplete_intervals; unmeasured_tail_hours is the age of the unbracketed tail after the latest profile; incomplete_days is always null |
Two caveats the numbers cannot escape: the lifetime counter includes some
modes the battle log never shows, so a ratio under 1.0 is an upper bound on
loss; and unmeasured_tail_hours is not part of the ratio. Missing coverage is
unknown, not evidence of absence.
players_timeline adds snapshots_available_from, and a line in notes[],
when you ask for dates before snapshots began.
Participation by week
clans_participation({ clan_tag?, weeks? }) answers, for every open
member of a clan in one call, what they did week by week: the raw material
for a clan's own participation rules, which Elixir does not have. It
measures; it never rates.
| Field | Meaning |
|---|---|
weeks[] |
the ISO weeks covered (iso_week, from, to, complete); Monday 00:00 UTC to Monday, the current week partial |
war_weeks[] |
the clan's recorded war weeks inside the window with their observed bounds; war weeks run on the game's grid, not ISO weeks |
members[].battles, ranked_battles, donations |
columns aligned to weeks[], one entry per ISO week in order; donations is the game's weekly counter as of the last daily snapshot in the week, null with no snapshot |
members[].war_decks, war_points, war_decks_by_day, war_battles_by_day |
columns aligned to war_weeks[]; war_decks_by_day holds war days 1 to 4 from roster polls during the day (null where the day was not polled) and war_battles_by_day the member's recorded war battles each day. verbosity: "compact" keeps only war_decks |
members[].joined_observed_at, tenure_known, days_in_clan_observed |
when the record first saw them in the clan; tenure_known is false for a member already present at the first roster poll, whose observed days are a lower bound |
members[].last_battle_time, days_since_battle |
the last recorded battle in any clan, and its age |
recording_active_since, first_roster_observed_at |
the recording horizon for the clan |
Null is unknown, never zero, throughout: a week with no snapshot has
donations: null, a war day nobody polled has decks_used_today: null,
a member with no recorded battle has days_since_battle: null. Counts
cover recorded battles only; elixir_coverage per tag says how complete
a member's log is. weeks is 1 to 8 (default 5). The per-member values
are columns rather than rows so a full clan over eight weeks fits the
response cap; should it not, the result_too_large hint names weeks
and verbosity: "compact".
Reading the game live
live: true is a request for a read of the game no older than the API's
own cache: 60 seconds for players, battle logs and boards, 120 for clans
and the river race. It is asynchronous. If such a read is in hand, the
tool answers from it (live_status.state: "fresh"). If not, one priority
fetch is queued for the next collector that checks in and the tool answers
now from the record as it stands, with live_status: { state: "pending", retry_after_s } and a note saying so; call again after that and the fresh
view is there. A subject with no record at all answers live_pending with
the same retry_after_s. Nothing waits on a collector inside a call.
7 recorded tools take the flag:
players_profile, battles_query, clans_roster, war_current, rankings_clan_ladder, rankings_clans, rankings_players. The player tools can read any tag;
clans_roster and war_current can read any clan, recorded or not;
the board tools name their board and location. battles_query polls a
player's battle log for the "what did they just play" path. Prefer these;
they are the live lane with the record's shape.
live_fetch({ path }) is the raw catch-all: one authenticated GET against
the Clash Royale API through the live lane, recorded on the way back. It
needs the payload itself, so it answers live_pending until one is in
hand, then { path, live: true, live_status, data, meta }.
Allowed path |
Recorded as |
|---|---|
/players/{tag} |
player |
/clans/{tag} |
clan |
/clans/{tag}/currentriverrace |
currentriverrace |
/clans/{tag}/riverracelog |
riverracelog |
/locations/{id}/rankings/players |
rankings_players (id is global or numeric) |
/locations/{id}/pathoflegend/players |
rankings_pol |
/players/{tag}/battlelog is refused with result_too_large before the
lane is spent: a raw battle log cannot fit the 48,000-character delivery cap,
so the fetch would cost a live call and deliver nothing;
battles_query({ player_tag, live: true }) polls the log once and answers in
the compact recorded shape. Anything else is bad_request. The response is
{ path, live: true, data, meta } with data the raw payload: card levels
there are on the API's rarity-relative scale (a maxed legendary reads 8/8),
while every recorded tool serves the in-game 1 to 16 scale; cards_catalog
carries both maxima. live_unavailable is answered only when the lane is
not configured or the fresh payload was refused at admission.
Live fetches are capped per day by tier (20 / 100 / 250 / 1,000; owner and admin unlimited) and an agent spends its owner's allowance. A queued fetch is charged once, when it is queued; a fresh read already in hand and the follow-up call that finds it are free. Every collector in the fleet picks up a queued live fetch first, so the worst-case wait is one check-in interval (15 seconds) plus the fetch. Leaderboard fetches also enrol the ranked tags into the corpus.