# Card

> A single printed card: identity, printing, artwork, set membership and the ids that link it to external marketplaces.

Source: https://pokemontcgapi.com/docs/objects/card

Returned by every card endpoint. Without `select` you get every scalar field the catalogue holds; with `select` you get exactly the names you asked for, plus `id`, which is always present.

Relations (`prices`, `translations`, `images`, `set`, `artist`) are absent unless you ask for them with `include`. Each one is a join, so ask only for what you render. There is no `legalities` relation: `include=legalities` is rejected with 400 `INVALID_INCLUDE`, because a relation that always came back empty would read as a card with no legal format rather than as a gap in the catalogue.

The game-text fields carry rows since 2026-09-03. The rates below are measured, not estimated, and counted over the whole catalogue — all 52,337 cards, not a sample. They look low because they are honest: the game text we hold is English, so it lands on the 20,725 Western printings and not on the Japanese and Chinese ones, which are the larger half of the catalogue. Against Western cards alone, `attacks` is on 83% of them.

## Fields

| Field | Type | Null | Description |
| --- | --- | --- | --- |
| `id` | string | no | Public id, `{set_code}-{number}`. Stable, unique and guessable — `bs-4` is the Base Set Charizard. |
| `legacy_id` | string | yes | Alternate string id in `{set}-{number}` form (`base1-4`). Accepted anywhere an id is accepted, so ids you already store keep working without a translation table. |
| `name` | string | no | Printed name. Localised when `lang` is set, with English as fallback. |
| `number` | string | no | Collector number as printed. A **string**: `TG12`, `SV107`, `1S` and `H1` all exist. |
| `number_sort` | integer | yes | Numeric part extracted for sorting. Null on numbers with no leading digits. |
| `supertype` | string | yes | `Pokémon`, `Trainer` or `Energy`. Three values, and `GET /v1/reference` returns the live list under `supertypes`. |
| `subtypes` | string[] | no | Printed subtypes, recomposed into one array — stage, suffix, Trainer kind and Energy kind all land here, so `q=subtypes:"Stage 2"` finds every Stage 2 printing. Populated on **38%** of the catalogue (20,082 cards), 26 distinct values. |
| `types` | string[] | no | Energy types. Populated on **43%** of the catalogue (22,363 cards). Empty where we hold no game text for the printing, which is mostly the Japanese and Chinese ones. |
| `hp` | integer | yes | Hit points. Populated on 100% of the sample. Null for cards with no printed HP. |
| `level` | string | yes | Reserved; still empty after the 2026-09-03 enrichment, because no source we hold carries it. |
| `evolves_from` | string | yes | Name of the printing this one evolves from. **14%** of the catalogue (7,158 cards) — that is the share that evolves from something, not a coverage gap. |
| `evolves_to` | string[] | no | Names this one evolves into. **15%** (7,624 cards). Derived from the evolution graph rather than read off the card, so it names whatever evolves from this Pokémon anywhere in the catalogue. |
| `rarity` | string | yes | Printed rarity. Free text across eras and print lines — 67 distinct values on 2026-09-03, so read them from `GET /v1/reference` under `rarities` rather than hardcoding an enum. |
| `regulation_mark` | string | yes | Single letter used by Standard rotation. Present only on the modern print runs that carry one. |
| `flavor_text` | string | yes | The italic prose under the artwork. **20%** of the catalogue (10,284 cards); many printings simply do not carry one. |
| `abilities` | object[] | yes | Abilities, Poké-Powers, Poké-Bodies and their kin. **8%** of the catalogue (4,042 cards) — the real share of printings that have one. Shape: `name`, `text`, `type`. |
| `attacks` | object[] | yes | Attacks in printed order. **33%** of the catalogue (17,207 cards), which is 83% of the Western printings. Shape: `name`, `cost`, `converted_cost`, `damage`, `text`. `damage` is a **string**: `100`, `180+` and `30×` all occur. |
| `weaknesses` | object[] | yes | Weaknesses. **31%** of the catalogue (16,124 cards). Shape: `type`, `value` — and `value` is a string that carries its own operator, `×2` or `+20`. |
| `resistances` | object[] | yes | Resistances, same shape as weaknesses. **9%** (4,741 cards): most printings have none. |
| `retreat_cost` | string[] | no | One entry per energy needed to retreat, as symbols. **31%** of the catalogue (16,161 cards); also empty on the printings that retreat for free. |
| `converted_retreat_cost` | integer | yes | The length of `retreat_cost`, precomputed so a range filter is possible. **33%** (17,061 cards) — slightly more than `retreat_cost`, because a free retreat is `0` here and `[]` there. |
| `rules` | string[] | no | Rule box text, which for a Trainer is the card effect. **6%** of the catalogue (2,951 cards). |
| `national_pokedex_numbers` | integer[] | no | National Pokédex numbers. **42%** of the catalogue (21,738 cards). Multiple entries on cards depicting more than one Pokémon. |
| `tcgplayer_id` | integer | yes | External marketplace id, for joining against data you already licence. Present for joining, not a licence to resell their data. |
| `cardmarket_id` | integer | yes | External marketplace id, same caveat. |
| `jp_twin_id` | string | yes | The Japanese printing of the same card, as an id you can request back. Present where the pairing is known. (This row read `tcgdex_id` until 2026-09-03; the service has never returned that name.) |
| `index_eur` | number | yes | Our composite index in EUR, denormalised onto the card so you can sort and filter on price in the same query that reads the card. The row behind it is the `PTCG_INDEX` entry under `include=prices`. |
| `last_price_at` | timestamp | yes | When a price was last observed for this card. Sortable, so "recently repriced" is one query. |
| `row_version` | integer | no | Incremented by a database trigger on every real update, so an unchanged re-import does not bump it. |
| `created_at` | timestamp | no | ISO 8601, UTC. |
| `updated_at` | timestamp | no | ISO 8601, UTC. |
| `set_code` | string | no | Code of the owning set. Denormalised so the common render needs no `include`. |
| `set_name` | string | no | Name of the owning set. |
| `set_total` | integer | yes | Cards in the set including secrets. |
| `ptcgo_code` | string | yes | Set code as used by the game client, e.g. `BS`. |
| `release_date` | date | yes | `YYYY-MM-DD`. A date, never a timestamp — the hour was never recorded. |
| `print_region` | string | no | `WEST`, `JP` or `CN`. `KR` is a valid value in the schema and matches no rows today. |
| `series` | string | yes | Series name, e.g. `Base`. Null on print lines whose series we have not mapped — most Japanese sets today. |
| `artist_name` | string | yes | Illustrator name. Named `artist_name`, not `artist`: `artist` belongs to `include`, and a field that changes type depending on another parameter is what breaks typed clients. |
| `artist_slug` | string | yes | Slug for `/v1/artists/{slug}`. |

## Example

`card.json`

```json
{
  "id": "bs-4",
  "legacy_id": "base1-4",
  "name": "Charizard",
  "number": "4",
  "number_sort": 4,
  "supertype": "Pokémon",
  "subtypes": ["Stage 2"],
  "types": ["Fire"],
  "hp": 120,
  "level": null,
  "evolves_from": "Charmeleon",
  "evolves_to": [],
  "rarity": "Rare Holo",
  "regulation_mark": null,
  "flavor_text": "Spits fire that is hot enough to melt boulders. Known to unintentionally cause forest fires.",
  "abilities": [
    {
      "name": "Energy Burn",
      "text": "As often as you like during your turn (before your attack), you may turn all Energy attached to Charizard into Fire Energy for the rest of the turn. This power can't be used if Charizard is Asleep, Confused, or Paralyzed.",
      "type": "Pokemon Power"
    }
  ],
  "attacks": [
    {
      "name": "Fire Spin",
      "cost": ["Fire", "Fire", "Fire", "Fire"],
      "converted_cost": 4,
      "damage": "100",
      "text": "Discard 2 Energy cards attached to Charizard in order to use this attack."
    }
  ],
  "weaknesses": [{ "type": "Water", "value": "×2" }],
  "resistances": [{ "type": "Fighting", "value": "-30" }],
  "retreat_cost": ["Colorless", "Colorless", "Colorless"],
  "converted_retreat_cost": 3,
  "rules": [],
  "national_pokedex_numbers": [6],
  "tcgplayer_id": 42382,
  "cardmarket_id": 273699,
  "jp_twin_id": "exp-021",
  "index_eur": 556.23,
  "last_price_at": "2026-09-02T00:00:00.000Z",
  "row_version": 6,
  "created_at": "2026-05-23T14:37:01.304Z",
  "updated_at": "2026-09-03T00:17:04.527Z",
  "set_code": "bs",
  "set_name": "Base",
  "set_total": 134,
  "ptcgo_code": "BS",
  "release_date": "1999-01-09",
  "print_region": "WEST",
  "series": "Base",
  "artist_name": "Mitsuhiro Arita",
  "artist_slug": "mitsuhiro-arita"
}
```

## What this object is for

The catalogue is an identity and provenance layer first: which printing this is, which set and region it belongs to, who drew it, what it is worth and which external ids it joins to. Since 2026-09-03 it also carries the game text — attacks, abilities, weaknesses, subtypes — for the printings we hold it for.

Until 2026-09-03 the rules-text fields were declared and empty, and this page said so. They now carry rows on the Western printings — the example above is a real response, Energy Burn and all — while the Japanese and Chinese ones still have none. Each field above states its own measured rate rather than a single headline number, because a share quoted over the whole catalogue and a share quoted over the cards it can apply to are different claims.

## Getting prices onto a card

Two ways, both live. `index_eur` and `last_price_at` are already on every card row, so a price-sorted list costs one request. For the full per-source breakdown, ask for the relation:

```bash
GET /v1/cards/base1-4?include=prices
```

Each entry is a [price](https://pokemontcgapi.com/docs/objects/price) object with its own source, basis, currency and date.
