# Versioning

Your integration keeps answering the way it did when you built it. A
request names the contract version it expects, and the API returns that
version's shape, even after a newer version changes it.

The API serves 2 dated versions: `2026-10-21`, `2026-10-26`. The latest is `2026-10-26`.

## The header

A version is a date, `YYYY-MM-DD`. It travels in one request header and
nowhere else:

```bash
curl "https://api.cardog.app/v2/vin/2T3R1RFV7MW180266" \
  -H "x-api-key: $CARDOG_API_KEY" \
  -H "Cardog-Version: 2026-10-26"
```

- **No header** returns the oldest version still served, so an integration
  that never sends the header is never broken by a new version. It moves
  forward only when that version reaches its sunset. Send the header to get
  a newer shape.
- **A served date** returns that date's shape.
- **Your key never selects a version.** Rotating a key never changes an
  answer, and two services on one key can each ask for their own version.

Every `/v2` response says what it served:

| header | means |
| --- | --- |
| `Cardog-Version` | the version this response was shaped to |
| `Cardog-Latest-Version` | the newest version the API serves |

When the two differ, a newer version exists. The [changelog](https://cardog.app/changelog)
says what changed and why.

## The SDKs pin for you

`@cardog/api` and the `cardog` Python package send `Cardog-Version`
on every `/v2` request. The value is `PINNED_API_VERSION`, the version the
SDK release was built against. Upgrading the SDK is how you move to a newer
version, and the response types match the version it pins.

```ts
import { CardogClient, PINNED_API_VERSION } from "@cardog/api";

const client = new CardogClient({ apiKey: process.env.CARDOG_API_KEY });
await client.v2.vin.getByVin("2T3R1RFV7MW180266");
client.lastVersion; // { requested, latest } from the response headers
```

To speak another version on purpose, while you migrate, set `apiVersion`
(`api_version=` in Python). The types then describe the pinned version
only, so validate what you read.

## How long a version is served

A version is served for **12 months after the next
version lands**. A response pinned to a version that has been superseded
says so in two more headers:

| header | means |
| --- | --- |
| `Deprecation` | when the newer version landed, as `@` and Unix seconds |
| `Sunset` | the first day this version is no longer served |

with a `Link` header pointing at the migration guide for the newer
version, `https://cardog.app/docs/migrations/<date>`.

## Errors

| status | code | when |
| --- | --- | --- |
| `400` | [`invalid_request`](https://cardog.app/docs/errors/invalid_request) | the header is not a date, or names a version the API does not serve. `hint` lists the served versions |
| `409` | [`version_sunset`](https://cardog.app/docs/errors/version_sunset) | the version is past its window. `hint` names a served version and links the migration guide |

A version request is checked before anything is metered, so neither error
costs credits.

## What counts as a new version

A new version is cut only for a change that would break a working
integration: a field removed, renamed or retyped, or a value that changes
meaning. Adding a field, an operation or an error code is not a new
version, so dispatch on the codes you know and ignore fields you do not
(see [Conventions](https://cardog.app/docs/conventions)).

## v1

v1 is not dated. It is deprecated as of 2026-10-09, stops
answering on 2027-12-31, and says so on every response in the
same `Deprecation`, `Sunset` and `Link` headers. See the
[v1 overview](https://cardog.app/docs/v1).

---

Previous: [Errors](https://cardog.app/docs/errors.md) (Getting started)
Next: [Units](https://cardog.app/docs/units.md) (Getting started)
