# Versioning

> One path prefix, what it guarantees, what may change without notice, and how a deprecation would be announced.

Source: https://pokemontcgapi.com/docs/versioning

There is one prefix, `/v1`, and it is in the path and nowhere else. No version header, no `Accept` negotiation, no default that shifts under you, and no second shape to choose between.

```text
https://api.pokemontcgapi.com/v1/...   { data, meta, links } — cursors, snake_case keys
```

A request without the prefix is not routed to a default. `/cards` returns `404 ROUTE_NOT_FOUND`, which is louder than quietly serving whatever the newest shape happens to be.

## What the prefix guarantees

Within `/v1`, none of the following will change:

- A field that exists will not be removed, renamed, or change type.
- A field that is non-null today will not start returning null.
- An `error.code` will not change meaning, and a condition that returns one code today will not start returning a different one.
- An HTTP status for a given failure will not change.
- The order of paginated results for a given `orderBy` will stay deterministic.
- Default values — `limit=50`, `orderBy=id` — will not change.

### What may change at any time

- **New fields may appear** in any object. Parse permissively — a strict decoder that rejects unknown keys will break on a Tuesday.
- **New optional parameters** may be added. Existing behaviour without them is unchanged.
- **New `error.code` values** may appear as failure modes become distinguishable. Handle the codes you care about and fall back on the status class.
- **`error.message` wording** may change. Never parse it.
- **Catalogue data** changes constantly. That is the product.
- **Cursor contents** may change shape. They are opaque; nothing may depend on their encoding.

> **Additive is not breaking**
>
> This is the contract that lets us ship without a version bump. If your client tolerates unknown fields and unknown error codes, no ordinary release can break it.

## If a breaking change is ever needed

It goes in a new path prefix, served alongside `/v1` rather than replacing it. We would rather carry two prefixes than rewrite your parser for you. Concretely:

| Stage | What happens |
| --- | --- |
| Announcement | On the [changelog](https://pokemontcgapi.com/changelog), and by email to every account that used the affected route in the previous 30 days. |
| Deprecation | Responses gain `Deprecation` and `Sunset` headers (RFC 8594) carrying the removal date. Nothing stops working. |
| Minimum notice | **Six months** between the sunset header appearing and the route being removed. We would rather promise a window we can hold to than a longer one we cannot. |
| Removal | The route returns `404 ROUTE_NOT_FOUND`. A prefix is never repurposed for a different shape. |

`deprecation-headers.txt`

```text
Deprecation: Sun, 01 Mar 2026 00:00:00 GMT
Sunset: Tue, 01 Sep 2026 00:00:00 GMT
Link: </changelog>; rel="deprecation"
```

## Knowing what changed

| Source | Use |
| --- | --- |
| [Changelog](https://pokemontcgapi.com/changelog) | Human-readable, dated, one entry per release. |
| `Deprecation` / `Sunset` headers | Machine-readable. Log them and the alarm arrives months early. |
| `GET https://api.pokemontcgapi.com/v1/status` | Per-source data freshness and catalogue counts. Answers "is the catalogue stale" without emailing anyone. |

## Writing a client that survives

`permissive.ts`

```ts
// Permissive: extra keys are carried through instead of rejected.
interface Card {
  id: string;
  name: string;
  set_code: string;
  [key: string]: unknown;
}

function isCard(value: unknown): value is Card {
  if (typeof value !== "object" || value === null) return false;
  const row = value as Record<string, unknown>;
  // Only the fields you actually use. Validating fields you ignore turns
  // an additive release into an outage.
  return typeof row.id === "string" && typeof row.name === "string";
}
```

> **Do not pin to a date**
>
> There is no `?version=2026-08-17` parameter and there will not be one. Date-pinned APIs accumulate a version per customer and eventually cannot be changed at all. A path prefix with a stated sunset is slower to move and far easier to reason about.
