Kiwi API Documentation
Sign up, mint API tokens, host data, and run calculations.
Overview
Looking to sign up or manage your account and API keys? Use the developer portal. This page is the HTTP reference for calling the API programmatically.
The API is authenticated with API tokens. You create your account and mint tokens in the developer portal - a website you use in your browser - and your programs then send one of those tokens with every request.
| Where | What you do there |
|---|---|
https://dev.aallyn.net(browser) |
Sign up, sign in, and create / manage your API tokens |
https://api.aallyn.net(your code) |
The data & calculation API - authenticate with your API token |
Signing in happens in the portal, in your browser - never from a program. Your code authenticates only with an API token you generated there. There's no programmatic login endpoint; the token is the credential.
The full, interactive endpoint reference (try-it schemas, every field) lives on the API reference page, generated from the live OpenAPI spec.
Building with an AI assistant? Grab the
plain-text reference for LLMs - a single self-contained
file you can paste into ChatGPT, Claude, or Cursor as context. The raw machine spec is at
/openapi.json.
Get an API token
Everything in this step happens in the developer portal - a website, in your browser. Programs don't sign up or sign in; they only use the token you create here.
- Create an account. Open the developer portal and sign up - protected by a captcha and rate limiting. You'll get a verification email; confirm it before creating tokens.
- Sign in and open the Tokens tab.
- Create a token. Give it a name, choose its scopes
(e.g.
rotations:read,feeds:read) or grant all access, restrict it to the IP addresses your code runs from, and pick an expiry (30 / 60 / 90 days, or none). - Copy the secret. It looks like
kiwi_xxxxxxxx…_xxand is shown exactly once - store it in your secrets manager now. Only a hash is kept server-side, so it can never be shown again (rotate the token if you lose it).
Treat the token like a password. Never commit it to source control - leaked tokens are detected by secret scanning and automatically revoked.
Authenticating
Send your API token as a Bearer credential on every request to the data API. There's no login call and no session to manage - the token is the credential.
export KIWI_TOKEN="kiwi_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Once data endpoints are published, authenticate them like this:
curl https://api.aallyn.net/v1/<endpoint> \
-H "Authorization: Bearer $KIWI_TOKEN"
Each request is checked against the token's restrictions:
- A request from an IP that isn't on the token's allowlist →
403 ip_not_allowed. - A token missing a required scope →
403 insufficient_scope. - A revoked or expired token →
401 not_authenticated.
Data endpoints
Live Trove game data, grouped by function into nine categories, each with
its own scope: rotations (rotations:read) - server time,
bonuses, merchants, biomes; feeds (feeds:read) - news,
Twitch streams, YouTube / Bilibili videos, and the Trovesaurus event calendar; stats
(stats:read) - raw stat tables and class definitions; gems
(gems:read) - a gem simulator, evaluator, and build optimizer;
misc (misc:read) - a modding-software list and a time converter;
mods (mods:read) - decompile and build .tmod files;
updates (updates:read) - browse the archived game files
(versions, directory tree, single-file download);
codexes (codexes:read) - structured game data
(allies, mounts, dragons, mementos, recipes, items, fish, badges) parsed from the archive;
and btt (btt:read) - BetterTroveTools releases
(latest version per platform, per channel) for in-app update checks.
Rotations, feeds and stats are GET, read-only (timestamps are
real-UTC unix seconds); the gem and mod tools add POST calculators.
The API reference has the full field-level schema for each.
rotations, feeds, codexes and btt are public - callable with no token at a stricter per-IP rate limit (30 req/min/IP by default). Send a token carrying the scope to lift it to the full per-token limit (120 req/min). codexes gets 5× both budgets (150 req/min/IP anonymous, 600 req/min/token). Every other category requires a token with the matching scope.
| Endpoint | Scope | Returns |
|---|---|---|
/v1/rotations/server-time | rotations:read | Server time, current in-game day, next daily + weekly resets |
/v1/rotations/daily-buffs | rotations:read | Today's daily buff + the full Mon→Sun rotation |
/v1/rotations/weekly-buffs | rotations:read | This week's weekly buff + the 4-week rotation |
/v1/rotations/corruxion | rotations:read | Corruxion merchant: live timer + upcoming schedule |
/v1/rotations/fluxion | rotations:read | Fluxion merchant: voting/selling timer + schedule |
/v1/rotations/gardening | rotations:read | 2-day / 3-day plant harvest windows + what's next |
/v1/rotations/chaos-chest | rotations:read | Weekly Chaos Chest: featured item (relayed from Trovesaurus) + window + countdown |
/v1/rotations/calendar | rotations:read | Yearly calendar: all recurring rotations (buffs, merchants, gardening, biomes) as one ±365-day timeline |
/v1/rotations/delves?week= | rotations:read | A week's delve rotation - floor records relayed from a community delve source (default current week; /delves/weeks lists weeks). Gated behind the feature_delves_enabled admin toggle (default off; 404s when disabled) |
/v1/rotations/biomes | rotations:read | The 3-hour adventure-world biome rotation (current + upcoming) |
/v1/rotations/wild-mana | rotations:read | Weekly Wild Mana biome rotation |
/v1/rotations/stampy | rotations:read | Weekly Stampy event biome (48-hour window) |
/v1/feeds/news?limit= | feeds:read | Latest Trove news relayed from trovegame.com (1–50, default 20); full archive at /v1/misc/news-history |
/v1/feeds/twitch | feeds:read | Live Trove Twitch streams (channel, title, viewers, thumbnail) |
/v1/feeds/youtube | feeds:read | Recent Trove YouTube videos |
/v1/feeds/bilibili | feeds:read | Recent Trove Bilibili videos |
/v1/feeds/events | feeds:read | Ongoing Trovesaurus events (filter by ?category=) |
/v1/feeds/events/categories | feeds:read | Distinct event categories (discovered dynamically) |
/v1/feeds/events/upcoming · /history | feeds:read | Events not yet started / already ended (both filterable) |
/v1/stats/power-rank | stats:read | Power Rank stat table - each source and the PR it gives |
/v1/stats/magic-find | stats:read | Magic Find stat table - each source and the MF it gives |
/v1/stats/light | stats:read | Light stat table - each source and the Light it gives |
/v1/stats/classes | stats:read | All 18 classes as full objects, keyed by tech_name |
/v1/stats/classes/{tech_name} | stats:read | One class by its tech_name token (e.g. knight) |
GET /v1/gems/lookups | gems:read | Valid gem field values (tiers, types, elements, stats, augments, abilities) |
POST /v1/gems/generate | gems:read | Roll a gem (omit any field for random) → a gem object |
POST /v1/gems/augment | gems:read | Apply a focus augment to a stat by position → updated gem |
POST /v1/gems/spark · /flare | gems:read | Reroll a stat type / move a proc, by stat position → updated gem |
POST /v1/gems/level-up · /set-level | gems:read | Raise or set a gem's level → updated gem |
POST /v1/gems/evaluate | gems:read | Score a typed-in gem: quality %, Power Rank, cost to perfect |
GET /v1/gems/stat-range | gems:read | Plausible (min, max) value a stat can roll at |
GET /v1/gems/builds/options | gems:read | Valid build-config field values (classes, allies, foods, flags) |
POST /v1/gems/builds/calculate | gems:read | Top gem proc layouts for a build, ranked by damage coefficient |
GET /v1/misc/news-history?limit=&offset= | misc:read | Full Trove news archive (never pruned), newest first, paginated |
GET /v1/btt/releases?channel=&limit=&offset= | btt:read | BetterTroveTools GitHub releases (newest first; filter by release / beta channel) |
GET /v1/btt/latest?channel= | btt:read | Latest BTT version per platform (windows/linux/android); each platform walks back to find an asset |
GET /v1/btt/latest/{platform}?channel= | btt:read | Latest BTT version for one platform |
GET /v1/btt/check?installed=&platform=&channel= | btt:read | "Is there an update?" server-side version compare; returns { update_available, latest } so the client just reads a bool |
GET /v1/btt/changelog?limit_groups=&commits_per_group= | btt:read | Commits grouped by git tag (mirrors BTT's "Show changelog" button); newest first with an "Unreleased" group leading; cached so users don't trip GitHub's 60/hr unauth rate limit |
GET /v1/misc/software | misc:read | Third-party Trove modding software, grouped by category |
GET /v1/misc/timezones | misc:read | Timezones supported by the converter and clocks |
GET /v1/misc/time/now | misc:read | Current time across every zone, incl. Trove server (reset) time |
POST /v1/misc/time/convert | misc:read | Convert a time/zone (or unix) to every zone + Discord timestamp codes |
POST /v1/mods/read | mods:read | Decompile a .tmod (POST raw bytes); ?metadata_only= omits file contents |
POST /v1/mods/build | mods:read | Build a .tmod from header fields + files → raw bytes |
GET /v1/updates/branches | updates:read | Tracked branches (live-us / pts) with current version + file count |
GET /v1/updates/{branch}/versions | updates:read | Captured game version history, newest first |
GET /v1/updates/{branch}/tree?prefix= | updates:read | Directory listing, one level at a time (latest version) |
GET /v1/updates/{branch}/file?path= | updates:read | Download a single archived game file (streamed); /file/meta for hash + size |
GET /v1/codexes/types | codexes:read | Codex types present (ally, mount, dragon, memento, recipe, item, fish, badge) with counts |
GET /v1/codexes/search?q=&type=&category=&tradable=&sort= | codexes:read | Cross-type search/filter - the unified search surface; each result carries its type |
GET /v1/codexes/{type}?search=&category=&tradable=&sort= | codexes:read | Entries of one type - filter (name/description, category, tradable), sort, paginate |
GET /v1/codexes/{type}/categories | codexes:read | Distinct categories (+ counts) in a type, for filter dropdowns |
GET /v1/codexes/{type}/entry?path= | codexes:read | One codex entry by its source prefab path |
News, streams, videos, and events are relayed from upstream sources and
cached/stored server-side (refreshed on a timer), so your code hits one fast, consistent API.
Events are kept after they leave the upstream feed, so /events/history and
/events/upcoming work alongside what's ongoing; categories are free-form and
discovered dynamically.
Stat tables and classes are raw game data - transmitted as-is, no calculation. Each class
carries a stable tech_name token you can store and look it up by later.
The gem tools are stateless: nothing is stored
server-side. generate returns a gem object; you hold it and POST it back to an
action endpoint (augment, spark, flare,
level-up, set-level) with a stat_position (0, 1 or 2)
to mutate it, and it's returned updated. evaluate and builds/calculate
are pure calculators; the lookups and builds/options endpoints tell
you every valid field value to send.
The mod tools are stateless too. /mods/read
takes the raw .tmod bytes as the request body and returns its header properties
and file table (paths, sizes, FNV checksums); pass ?metadata_only=true to skip
the (base64) file contents. /mods/build takes header fields + files and returns a
freshly built .tmod as application/octet-stream - built in memory and
discarded once sent. The built mod's modLoader header is stamped
KiwiAPI. These two endpoints accept a larger 20 MB body.
Example: a merchant timer
curl https://api.aallyn.net/v1/rotations/fluxion \
-H "Authorization: Bearer $KIWI_TOKEN"
# → {
# "active": true,
# "state": "voting", // voting | selling | away
# "starts_at": 1717326000,
# "ends_at": 1717585200,
# "seconds_remaining": 46800, // to leaving when active, else to arriving
# "schedule": [ // upcoming windows, soonest first
# { "starts_at": …, "ends_at": …, "state": "voting" },
# { "starts_at": …, "ends_at": …, "state": "selling" }
# ]
# }
Example: the biome rotation
curl https://api.aallyn.net/v1/rotations/biomes \
-H "Authorization: Bearer $KIWI_TOKEN"
# → {
# "current": { "starts_at": …, "ends_at": …,
# "biomes": [ { "name": "Volcanic Fields", "final_name": "Dragonfire Peaks", "icon": "dragon" }, … ] },
# "upcoming": [ { "starts_at": …, "ends_at": …, "biomes": [ … ] }, … ]
# }
News refreshes on a background timer; everything else is computed
on demand. daily-buffs / weekly-buffs return the current buff
plus the whole rotation (full game data: name, color, emoji, buff lists, icons).
Rate limits
Limits are enforced per fixed window. Every rate-limited response carries
X-RateLimit-Limit, X-RateLimit-Remaining, and
X-RateLimit-Reset (unix seconds) headers so you can self-throttle.
When you exceed a limit you receive 429 with an additional
Retry-After header (seconds to wait).
| Surface | Default limit | Keyed by |
|---|---|---|
| Signup | 5 / hour | client IP |
| Login | 10 / 5 min | client IP |
| API requests | 120 / minute | API token |
Error responses
Every error returns a consistent JSON envelope with a stable, machine-readable
code you can branch on, a human-readable message, and
optional details.
{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded. Slow down and try again later.",
"details": { "limit": 120, "window_seconds": 60 }
}
}
| Status | code | Meaning |
|---|---|---|
400 | captcha_failed / bad_request | Captcha rejected or malformed request |
401 | not_authenticated | Missing, invalid, expired, or revoked credentials |
401 | invalid_credentials | Wrong email or password (login) |
403 | email_unverified | Must verify your email before minting tokens |
403 | forbidden | Authenticated but not allowed |
404 | not_found | Resource missing or not yours |
409 | email_taken | Email already registered |
422 | validation_error | Body failed validation (details lists fields) |
429 | rate_limited | Rate limit exceeded (see Retry-After) |
500 | internal_error | Unexpected server error |