> ## Documentation Index
> Fetch the complete documentation index at: https://docs.elestrals.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Identifiers

> Every ID prefix in the API and what it points at.

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

| Prefix | Resource      | Example   | Stable across reprints? |
| ------ | ------------- | --------- | ----------------------- |
| `sr-`  | Series        | `sr-10`   | n/a                     |
| `st-`  | Set           | `st-53`   | n/a                     |
| `cd-`  | Printing      | `cd-1813` | n/a                     |
| `et-`  | Card (Entity) | `et-100`  | Yes                     |
| `el-`  | Creature      | `el-159`  | Yes                     |
| `ble-` | Banlist entry | `ble-1`   | Yes                     |
| `rul-` | Ruling        | `rul-1`   | Yes                     |

"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:

```json theme={"dark"}
{
  "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:

| Field         | Example   | What it is                                                        |
| ------------- | --------- | ----------------------------------------------------------------- |
| `id`          | `cd-1813` | The API identifier. Use this in URLs.                             |
| `set_number`  | `100/180` | Printed on the card. Position within the set.                     |
| `sort_number` | `100`     | Numeric 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:

```bash theme={"dark"}
curl https://api.elestrals.com/v1/cards/banana \
  -H "Authorization: Bearer $ELESTRALS_KEY"
```

```json theme={"dark"}
{
  "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.
