Versioning
One path prefix, what it guarantees, what may change without notice, and how a deprecation would be announced.
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.
https://api.pokemontcgapi.com/v1/... { data, meta, links } — cursors, snake_case keysA 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.codewill 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
orderBywill 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, 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: 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 | 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: 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.