Skip to main content
Resource IDs in the API follow a consistent shape: a short letter prefix indicating the resource type, a hyphen, and a numeric suffix. Treat IDs as opaque strings — the prefix is informational; never compute on the suffix.

ID prefixes

PrefixResourceExampleStable across reprints?
sr-Seriessr-10n/a
st-Setst-53n/a
cd-Printingcd-1813n/a
et-Card (Entity)et-100Yes
el-Creatureel-159Yes
ble-Banlist entryble-1Yes
rul-Rulingrul-1Yes
“Stable across reprints” matters for any tool that builds a long-lived database keyed by these IDs:
  • Card and Creature IDs are forever. The et-100 that’s Pyrotter today will be Pyrotter on the day the API is sunset. New artwork, new printings, new banlist updates — the same et-100.
  • Printing IDs are also stable, but a Card can pick up new Printing IDs over time as it gets reprinted.
  • Banlist and Ruling IDs are stable for as long as the entry exists. If a ruling is superseded, its ID is retired (not reused) and a new ruling with a new ID takes its place.

Set codes vs Set IDs

Sets have two identifiers, and they’re different:
{
  "id": "st-53",
  "abbr": "BLE",
  "name": "Berserk Legends Edition"
}
  • id — the API identifier. What you pass in URLs: GET /v1/sets/st-53.
  • abbr — the human-friendly code, typically two to four uppercase letters. What appears on the bottom of physical cards. Use this for display, not for lookups.
The same applies to Series (series.code is the human-friendly Roman-numeral or short code; series.id is the API identifier).

Printing collector numbers

A printing has three number-ish fields, only one of which is the API identifier:
FieldExampleWhat it is
idcd-1813The API identifier. Use this in URLs.
set_number100/180Printed on the card. Position within the set.
sort_number100Numeric form of set_number for stable ordering across reprints.
If a user types a card number from a physical card into your search box, they’re typing set_number (not id). The list endpoint’s ?set_number= filter takes that exact format.

Internal IDs we don’t expose

The PlanetScale database has its own internal IDs — primary keys generated for normalized tables, foreign-key columns, etc. None of those leak into the public API. If you see an integer where a string ID would normally appear, that’s a bug — open a ticket with the request_id. The only reason this matters: don’t try to reverse-engineer the suffix as a primary key from an internal system. The numeric portion is monotonic but not contiguous (gaps appear when rows are deleted or merged) and not stable across migrations.

ID formats are validated

The router validates ID format before any database lookup. A request with a malformed ID short-circuits to a not_found without consuming a database round-trip:
curl https://api.elestrals.com/v1/cards/banana \
  -H "Authorization: Bearer $ELESTRALS_KEY"
{
  "error": {
    "code": "not_found",
    "message": "Card not found.",
    "request_id": "req_01H..."
  }
}
This is intentional — keeps probing for non-existent IDs cheap on our side and on yours.