OverwolfSpire Codex is now on Overwolf. Learn more →

Slay the Spire 2 (sts2) Developers

Build tools, bots, and content with Spire Codex data. Everything is free and open.

Tooltip Widget

Add Wowhead-style hoverable tooltips for cards, relics, and potions to any website. One script tag, zero dependencies.

Installation

<script src="https://spire-codex.com/widget/spire-codex-tooltip.js"></script>

Syntax

[[Strike]]Card tooltip (default type)
[[card:Bash]]Card (explicit)
[[relic:Burning Blood]]Relic
[[potion:Fire Potion]]Potion
[[character:Ironclad]]Character
[[monster:Jaw Worm]]Monster
[[power:Strength]]Power
[[event:Neow]]Event
[[encounter:Lagavulin]]Encounter
[[enchantment:Sharp]]Enchantment
[[keyword:Exhaust]]Keyword
[[orb:Lightning]]Orb
[[affliction:Bound]]Affliction
[[achievement:Minimalist]]Achievement

JavaScript API

SpireCodex.scan()Re-scan the page for new [[...]] patterns (for SPAs)
SpireCodex.scan(element)Scan a specific DOM element

Changelog Widget

Embed a compact, interactive changelog viewer showing Spire Codex update history with version switching.

Installation

<div id="scx-changelog"></div>
<script src="https://spire-codex.com/widget/spire-codex-changelog.js"></script>
data-version="1.0.4"Show a specific version (default: latest)

REST API

Full game database accessible via a public REST API. No authentication required: the per-IP website allowance covers browsing and casual calls, and keyless API usage gets the base rate below. For scripts and tools, create an API key and send it as the X-API-Key header. Create one on your profile page. A key buckets your requests by identity instead of IP (stable across networks, never shared with other users behind the same NAT) and gives you usage tracking; its tier cap applies per endpoint. Responses carry X-RateLimit-Remaining / X-RateLimit-Reset so you can pace requests.

Rate limits

Website Traffic300/minuteper IP
No API key15/minute
Registered User60/minutecreate one on your profile
Academia100/minutegranted on request
Paid120/minutesupporters

All caps count per endpoint: the table is your allowance on each route, not a global pool. A few heavy endpoints carry their own budgets that apply to every caller regardless of key (see the run export below; the language ZIP is 10/hour). Watch X-RateLimit-Remaining and back off on 429 (Retry-After is set).

Bulk run export

GET /api/exports/runs?limit=1000&start=2026-06-01T00:00:00Z

Streams official runs as JSONL, one run per line.

limit Bounds the page; a full page returns a cursor for the next one.
start / end Restrict to a half-open submitted-at window. Keep them constant while paging; the cursor does not embed them.

This endpoint has its own budget of 120 credits per hour, shared by keyed and anonymous callers alike: a paginated request costs 1 credit, an unbounded pull (no limit) costs 60. Sustained syncing therefore works best as paginated pulls spaced ~30s apart; on 429, honor Retry-After and resume with the same cursor.

Need the whole corpus? A call with no parameters redirects to a static file rebuilt once a day, /exports/runs-latest.jsonl.gz, with the same line shape; /api/exports/runs/manifest reports when it was generated and how many runs it holds. Each line carries player_token, a stable pseudonymous id for the submitting account, null on anonymous runs.

Just one patch? /api/exports/runs?version=v0.111.0 redirects to a per-version static file from the manifest's versions map, /exports/runs-v0.111.0.jsonl.gz; combined with limit and cursor it pages a single patch instead.

Base URL

https://spire-codex.com

Beta channel (unreleased content from the Steam beta branch): add this to any entity endpoint: ?channel=beta. Current beta version: /api/beta/version. Full diff against main: /api/beta/diff.

The community stats pages were reorganized: /community-stats, /leaderboards/metrics, /charts, and the /meta page now live under /stats, /leaderboards/elo is /top-players, and run submission moved to /runs/submit. Old URLs redirect.

Endpoints

Full interactive docs (auto-generated from the backend, always current): /docs

Entities

GET/api/cardsAll cards (filter: color, type, rarity, keyword, tag, spawns, search)
GET/api/cards/{id}Single card
GET/api/charactersAll characters
GET/api/characters/{id}Single character
GET/api/relicsAll relics (filter: rarity, pool, ancient, search)
GET/api/relics/{id}Single relic
GET/api/potionsAll potions (filter: rarity, pool, search)
GET/api/potions/{id}Single potion
GET/api/monstersAll monsters (filter: type, search)
GET/api/monsters/{id}Single monster
GET/api/powersAll powers (filter: type, stack_type, search)
GET/api/powers/{id}Single power
GET/api/eventsAll events (filter: type, act, search)
GET/api/events/{id}Single event
GET/api/encountersAll encounters (filter: room_type, act, search)
GET/api/encounters/{id}Single encounter
GET/api/enchantmentsAll enchantments
GET/api/enchantments/{id}Single enchantment
GET/api/keywordsCard keywords
GET/api/keywords/{id}Single keyword
GET/api/intentsAll intent types
GET/api/intents/{id}Single intent
GET/api/orbsAll orb types
GET/api/orbs/{id}Single orb
GET/api/afflictionsAffliction types
GET/api/afflictions/{id}Single affliction
GET/api/modifiersCustom mode modifiers
GET/api/modifiers/{id}Single modifier
GET/api/achievementsAll achievements
GET/api/achievements/{id}Single achievement
GET/api/badgesAll badges
GET/api/badges/{id}Single badge
GET/api/epochsAll epochs
GET/api/epochs/{id}Single epoch
GET/api/storiesAll stories
GET/api/stories/{id}Single story
GET/api/actsAll acts
GET/api/acts/{id}Single act
GET/api/ascensionsAll ascension levels
GET/api/ascensions/{id}Single ascension
GET/api/glossaryAll glossary terms
GET/api/glossary/{id}Single term

Aggregations & Lookups

GET/api/statsEntity counts
GET/api/ancient-poolsAll ancient relic pools with conditions
GET/api/ancient-pools/{id}Pools for a single ancient
GET/api/unlocksUnlockable entities grouped by type with epoch context
GET/api/history/{entity_type}/{entity_id}Per-entity version history from changelogs
GET/api/update-history/{entity_type}/{entity_id}Per-entity game-patch update history
GET/api/names/{entity_type}/{entity_id}Cross-language name lookup for an entity
GET/api/searchUnified site search across entities, reference entries, mechanics, guides, news, site pages and image files, with typo tolerance (q, lang)
GET/api/search/semanticMeaning-based entity search, same item shape as /api/search (q, limit); empty when the feature is off
GET/api/merchant/configMerchant pricing config extracted from the game
GET/api/mechanics/constantsMechanics constants: room odds, gold ranges, ascension tuning
GET/api/mechanics/sectionsIndex of the mechanics pages
GET/api/mechanics/sections/{slug}One mechanics page as markdown with template tokens resolved
GET/api/changelogsAll changelogs
GET/api/changelogs/recent-additionsNewest entities surfaced for the homepage band
GET/api/changelogs/{tag}Single changelog by tag (e.g. v1.0.20)
GET/api/newsSteam announcements (mirrored locally for permanence)
GET/api/news/{gid}Single news article with sanitized body
GET/api/versionsAvailable beta data versions for the version picker
GET/api/news/codexSpire Codex site news, newest first
GET/api/news/codex/{slug}One site news entry
GET/api/announcementsActive site banner messages

Languages

GET/api/languagesAvailable languages with display names
GET/api/translationsTranslation maps for filter values and UI strings (lang)
GET/api/localizationsThe game's own localization tables as ICU MessageFormat, all of them or the ones named in ?tables= (max 50; unknown names are 404)
GET/api/localizations/tablesNames of the available localization tables
GET/api/localizations/{table}One localization table

Community & Submissions

GET/api/guidesAll guides (filter: category, difficulty, tag, search)
GET/api/guides/{slug}Single guide with rendered markdown
POST/api/guidesSubmit a guide (Discord webhook, rate-limited)
POST/api/runsSubmit a run for community stats and leaderboards. The run is attached to an account only with Authorization: Bearer <jwt>; a bare steam_id is kept as a hint
POST/api/runs/claimAttach the signed-in account to previously-submitted anonymous runs by hash
GET/api/runs/listBrowse submitted runs with filters and pagination (incl. ascension_min/ascension_max and winrate_min/winrate_max by submitter win rate — the content brackets) Each row carries player_token, a stable pseudonymous id for the submitting account, null on anonymous runs. total_is_lower_bound is true when the exact count ran out of time and total is a capped lower bound.
GET/api/runs/leaderboardRun leaderboards (fastest, highest_ascension); filter by character, players, game_mode, ascension_min, winrate_min (the content brackets), build_id
GET/api/runs/shared/{run_hash}Single submitted run by hash (rate-limited)
GET/api/runs/statsAggregate community stats (filter by character, ascension, username)
GET/api/runs/community-statsThe /stats hub dataset: event decision splits, deadliest encounters, win rates by ascension/character, records. Takes the same bracket keys as the metrics tables; an unknown bracket returns HTTP 400
GET/api/charts/metaChart registry for the /stats/charts explorer: available charts, filters, splits, and run stats
GET/api/charts/{chart}One pre-aggregated chart (filter: players, ascension, game_mode, username, split, bracket=a10|wr30|wr50|wr75, build_id, plus per-chart params). A saved chart id from the chart builder returns that saved chart instead
POST/api/chartsSave a chart spec to your profile (bearer token, 100 per account)
PATCH/api/charts/{id}Update the title, visibility or spec of your own saved chart; DELETE removes it
GET/api/charts/mineYour saved charts, newest first (bearer token)
GET/api/charts/publicNewest public saved charts (limit)
GET/api/tierlists/shared/{share_id}Public read-only view of a shared tier list
GET/api/beta/diffWhat the current beta adds, changes, and removes per entity type; powers every BETA label
GET/api/beta/versionThe current beta version
GET/api/runs/scores/{type}Codex Score + Codex Elo per entity (cards/relics/potions); ?bracket= grades within a run bracket (a10|wr30|wr50|wr75, a game version, or any metrics bracket; unknown is HTTP 400) (the in-game mod sends the same via ?stat_filter=a10|a10_wr30|a10_wr50|a10_wr75); relics accept ?act=1|2|3 to rank by acquisition act; ?character= switches to that character's slice (entries gain a scope field)
GET/api/runs/leaderboard/seed-rankSeed + global standing for one seed (?seed=&steam_id=); rank fields are null without a winning run
POST/api/auth/steam/ticketExchange a Steamworks web auth ticket for the site JWT (in-game silent sign-in); 503 until the server has a Steam key
GET/api/runs/metrics/{type}Dense metrics table for cards/relics/potions, one row per entity: Codex Score, win_rate with a Wilson 95% win_rate_ci, hold_rate, lift and lift_n over the floor-adjusted expectation (null under 20 seats), Codex Elo and pick_rate from free choice screens for cards and relics, a wax block on relics (Toy Box copies split out), and used/use_rate on potions. Counts are per seat, so a 4P run is four seats; the response carries total_runs, total_seats, total_wins and data_through. ?bracket=all|solo|2p|3p|4p|a10|daily|custom|wr30|wr50|wr75 or a composite like solo:standard; ?character= switches to that character's slice (rows gain per-character Elo and Pick%). An unknown bracket returns HTTP 400
GET/api/runs/metrics/shopsShop shelf table, one row per card, relic or potion: seen, bought, buy_rate, and the buyers' win_rate, win_rate_ci and lift. Same bracket keys as the other metrics tables
GET/api/runs/metrics/eventsEvent option table: chosen, share of that event's choices, win_rate, win_rate_ci and lift. Same bracket keys as the other metrics tables
GET/api/runs/metrics/campfiresRest-site choice table (rest, smith, and the rest) with a localized name per ?lang=: chosen, share, low_hp_share, win_rate, win_rate_ci and lift. Same bracket keys as the other metrics tables
GET/api/runs/versionsDistinct game build IDs that have submitted runs
GET/api/runs/leaderboard/rank/{run_hash}Rank of one winning run within its ladder (category)
GET/api/leaderboards/eloTop players by Codex Elo over solo A10 standard runs, refreshed nightly; powers /top-players (limit, min_runs)
GET/api/runs/stats/{type}/{id}Community stats for one card, relic or potion, with per-bracket and per-character splits
GET/api/runs/stats/{type}/{id}/historyDaily Codex Score and Elo history for one entity (bracket=all|a10|wr30|wr50|wr75)
GET/api/runs/top/{type}/{character}Most-picked entities of a type for one character (limit)
GET/api/runs/encounter-statsPer-encounter appearances, fatal rate, avg damage and turns, per character too (act, room_type, multiplayer, bracket, build_id, encounter, page, limit)
GET/api/runs/encounter-seriesThe given encounters (max 20) rolled up per bracket and per recent game version
GET/api/runs/pulseCommunity run and win totals plus data_through
GET/api/runs/snapshot-statusWhether the stats are loaded or rebuilding
GET/api/runs/{run_hash}/similarNearest winning solo decks to a run and what they took that it didn't
GET/api/runs/archetypesCommunity deck archetypes, clustered nightly
GET/api/runs/encounter-buildsWhich archetypes die to an encounter and which walk past it (encounter)
GET/api/runs/deck-advisorWhat the winning decks nearest a partial deck carry that it doesn't yet (character, cards, relics)
GET/api/runs/pick-coachNearest archetypes for a partial deck and a score per offered card (character, cards, relics, offer, target)
GET/api/pairings/{item_type}/{item_id}Cards, relics and potions that show up in the same runs as this item
GET/api/draft-recs/{item_type}/{item_id}Cards players most often draft when they already hold this item
POST/api/draft-adviceRank the offered cards for the current deck
GET/api/replaysRuns with a replay to watch; same filters and row shape as /api/runs/list plus replay_url
GET/api/replays/summaryHow many replays are watchable, overall and per character
GET/api/runs/{run_hash}/replayThe stored replay journal as gzip; POST uploads one for a run you own, DELETE removes it
GET/api/players/{username}/insightsOne player's public insights with community comparison; 404 for unknown or private profiles
GET/api/players/{username}/statsOne player's own cards, relics, potions, events, shops and campfire stats with personal Lift; same filters and privacy as insights
GET/api/presence/activeLive runs from the in-game mod, deepest first
GET/api/presence/{steam_id}One player's live run; 404 when they aren't in one
GET/api/thanksGitHub contributors, special thanks, and public supporters
GET/api/mod/latestLatest mod build and the game version it was verified against
GET/api/rate-limitsThe public rate-limit tiers
POST/api/feedbackSubmit feedback (Discord webhook)

Bulk Downloads

GET/api/exports/runsJSONL stream of official runs (limit/start/end/cursor pagination; own 120-credits-per-hour budget - see Bulk run export above)
GET/api/exports/runs/manifestWhen the daily full run dump was generated and how many runs it holds
GET/api/exports/{lang}ZIP of all entity JSON for one language (10/hour)
GET/api/imagesImage gallery categories
GET/api/images/searchSearch images by filename
GET/api/images/game/{version}/{category}/browsePaged folder browsing of a full asset dump (path, offset, limit)
GET/api/images/game/{version}/{category}/downloadZIP of one dump folder's files (path; capped at 2000 files)

Multi-Language

Add this query parameter to any endpoint: ?lang=jpn. 15 languages supported:

eng, deu, esp, fra, ita, jpn, kor, pol, ptb, rus, spa, tha, tur, zhs, zht

Quick Start

cURL

curl https://spire-codex.com/api/cards?color=ironclad&rarity=Rare

Python

import requests
cards = requests.get("https://spire-codex.com/api/cards", params={"color": "ironclad"}).json()
for card in cards:
    print(f"{card['name']} - {card['type']} ({card['rarity']})")

JavaScript

const res = await fetch("https://spire-codex.com/api/relics?pool=ironclad");
const relics = await res.json();
console.log(relics.map(r => r.name));

Tiny Card Sprite

Reproduce the game's in-run card thumbnail (used on the Run History / Game Over screens) in any web project. Six PNG layers composited with CSS mask-image, no canvas, no WebGL, just tinted sprites. Colors come straight from the decompiled game code: NTinyCard, CardPoolModel.DeckEntryCardColor.

Preview

Ironclad / Attack / Common
Silent / Skill / Uncommon
Defect / Power / Rare
Necrobinder / Skill / Rare
Regent / Attack / Uncommon
Fool / Attack / Rare
Curse
Event
Quest

Sprite assets

All 10 PNGs are served with CORS enabled, drop the base URL in front of each filename. Each sprite is 128×128, RGBA, white-on-transparent (meant to be tinted via CSS).

https://spire-codex.com/static/images/ui/run_history_card/
  card_back.png           ← tinted by pool
  desc_box.png            ← dark description area (render at 25% opacity)
  attack_portrait.png     ← portrait per card type
  attack_portrait_shadow.png
  skill_portrait.png
  skill_portrait_shadow.png
  power_portrait.png
  power_portrait_shadow.png
  banner_shadow.png       ← render at 60% opacity
  banner.png              ← tinted by rarity

Pool (card back) colors

Source: CardPoolModel.DeckEntryCardColor. Match these against the color field returned by /api/cards.

ironclad#D62000
silent#5EBD00
defect#3EB3ED
necrobinder#CD4EED
regent#E36600
fool#D9CF3C
colorless#A3A3A3
event#A3A3A3
curse#585B61
quest#24476A
status#FFFFFF
token#FFFFFF

Rarity (banner) colors

Source: NTinyCard.GetBannerColor. Match against the rarity field.

Basic#9C9C9C
Starter#9C9C9C
Common#9C9C9C
Uncommon#64FFFF
Rare#FFDA36
Curse#E669FF
Event#13BE1A
Ancient#13BE1A
Quest#F46836
Status#9C9C9C
Token#9C9C9C

Minimal HTML + CSS recipe

Portrait filename: attack = Attack cards, power = Power cards, skill = everything else (Skill, Status, Curse, …)

<div class="tiny-card" style="
  --back: #D62000;   /* pool color, Ironclad */
  --banner: #FFDA36; /* rarity color, Rare */
  position: relative;
  width: 64px;
  height: 64px;
">
  <!-- 1. card back, tinted by pool -->
  <div class="layer" style="
    background-color: var(--back);
    mask: url(https://spire-codex.com/static/images/ui/run_history_card/card_back.png) center/contain no-repeat;
    -webkit-mask: url(https://spire-codex.com/static/images/ui/run_history_card/card_back.png) center/contain no-repeat;
  "></div>

  <!-- 2. description box -->
  <img class="layer" src="https://spire-codex.com/static/images/ui/run_history_card/desc_box.png" style="opacity:.25">

  <!-- 3. portrait shadow + portrait (attack/skill/power) -->
  <img class="layer" src="https://spire-codex.com/static/images/ui/run_history_card/attack_portrait_shadow.png">
  <img class="layer" src="https://spire-codex.com/static/images/ui/run_history_card/attack_portrait.png"
       style="filter: brightness(.95) sepia(.15)">

  <!-- 4. banner shadow + banner tinted by rarity -->
  <img class="layer" src="https://spire-codex.com/static/images/ui/run_history_card/banner_shadow.png" style="opacity:.6">
  <div class="layer" style="
    background-color: var(--banner);
    mask: url(https://spire-codex.com/static/images/ui/run_history_card/banner.png) center/contain no-repeat;
    -webkit-mask: url(https://spire-codex.com/static/images/ui/run_history_card/banner.png) center/contain no-repeat;
  "></div>
</div>

<style>
  .tiny-card .layer {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: contain;
  }
</style>

React component

Drop-in React version. Source: TinyCard.tsx

import TinyCard from "./TinyCard";

// Feed in the three fields from /api/cards:
<TinyCard color="ironclad" type="Attack" rarity="Rare" className="w-16 h-16" />

Full card images

Every card from /api/cards includes two ready-to-use URLs for the full game-rendered card (frame, art, banner, and text, exactly as it looks in-game). Ancient cards are animated webps.

  • image_url_card — The base card. null for the one card with no render (mad_science); fall back to image_url (the portrait art) there.
  • image_url_card_upg — The upgraded card. null when the card has no upgrade.
// e.g. /api/cards/bash
{
  "id": "BASH",
  "image_url":          "/static/images/cards/bash.webp",  // portrait art
  "image_url_card":     "https://cdn.spire-codex.com/cards-full/stable/bash.webp",
  "image_url_card_upg": "https://cdn.spire-codex.com/cards-full/stable/bash_upg.webp"
}

Localized renders live under a language subfolder, for example: cards-full/stable/jpn/bash.webp. All 15 languages are available.

Enchanted card renders

Every card is also rendered with each enchantment it can legally take, exactly as the in-game enchant preview draws it (badge, amount, and added card text). The URLs follow one pattern:

https://cdn.spire-codex.com/cards-full/stable/ench/{enchantment}/{card}.webp        // English, base
https://cdn.spire-codex.com/cards-full/stable/ench/{enchantment}/{card}_upg.webp    // English, upgraded
https://cdn.spire-codex.com/cards-full/stable/{lang}/ench/{enchantment}/{card}.webp // localized

// e.g. Anger with Corrupted, in Japanese:
https://cdn.spire-codex.com/cards-full/stable/jpn/ench/corrupted/anger.webp
  • {enchantment} / {card}: lowercase ids from /api/enchantments and /api/cards (e.g. sharp, corrupted, sown).
  • Only valid card and enchantment combinations exist (the export uses the game's own applicability rules), so an invalid combo is a 404. The card_type / applicable_to fields on /api/enchantments describe which cards qualify.
  • Base and upgraded variants exist for every combo, in all 14 languages, with the enchantment text fully localized.

Data Exports

Download all game data as a single ZIP archive. Each archive contains JSON files for every entity type (cards, relics, monsters, powers, and more).

Download English Data (ZIP)

15 languages available. Example downloads: Japanese, Korean, Chinese, French, German

Interactive API Docs

Full Swagger/OpenAPI documentation with try-it-out functionality.

Open API Docs →

Open Source

Spire Codex is open source. The data extraction pipeline, API, and frontend are all available on GitHub.

View on GitHub →