# Versioning

> What can change inside v1, and what happens before anything is removed.

The major version is in the path. Every endpoint begins `/v1/`, and that is the
only version marker: there is no version header, no date pinning, and no
`?version=` parameter to set.

## What can change inside v1 [#what-can-change-inside-v1]

Inside a major version we only add.

| Change                                                | Happens inside `/v1/` |
| ----------------------------------------------------- | --------------------- |
| A new endpoint                                        | yes                   |
| A new optional query parameter                        | yes                   |
| A new field on `Tweet`, `User` or a response envelope | yes                   |
| A new value in an `error.code` enum                   | yes                   |
| A field removed or renamed                            | no                    |
| A field's type changed                                | no                    |
| A parameter becoming required                         | no                    |
| A status code changing meaning                        | no                    |

**Parse defensively.** A new field is not a breaking change, so ignore what you
do not recognise rather than failing on it. Both SDKs already do. If you hand
responses to a strict schema validator, configure it to allow unknown keys.

## What happens before anything is removed [#what-happens-before-anything-is-removed]

A change that would break a caller opens `/v2/` instead of altering `/v1/`. When
that happens, `/v1/` keeps answering for **at least 12 months** from the day the
deprecation is announced, and every `/v1/` response carries the notice for the
whole of that period:

| Header                         | Meaning                                                 |
| ------------------------------ | ------------------------------------------------------- |
| `Deprecation`                  | The date the version was declared deprecated. RFC 9745. |
| `Sunset`                       | The earliest date it may stop answering. RFC 8594.      |
| `Link: <…>; rel="deprecation"` | What changed, and how to move.                          |

```http
Deprecation: Wed, 01 Jul 2026 00:00:00 GMT
Sunset: Thu, 01 Jul 2027 00:00:00 GMT
Link: <https://xdataapi.io/docs/versioning>; rel="deprecation"
```

**No header, no sunset.** The absence of `Sunset` on a response is the promise
that the version you are calling is not going away inside the notice period.
Nothing is ever removed from `/v1/` without it, so a client that watches for the
header needs no other signal. Today `/v1/` sends none: it is current.

Deprecations are also posted on [the status page](/status) and announced by email
to every account that called the affected endpoint in the previous 30 days.

## Checking from code [#checking-from-code]

```bash
curl -sS -D- -o /dev/null \
  -H "x-api-key: $XDATAAPI_KEY" \
  https://api.xdataapi.io/v1/me | grep -i '^sunset\|^deprecation'
```

An empty result means the version is current. Anything else is the date you have
to move by, and the `Link` header is where to read what changed.

## The machine-readable contract [#the-machine-readable-contract]

The same policy is stated in the OpenAPI document, which is the thing to generate
clients from:

* [https://xdataapi.io/openapi.json](https://xdataapi.io/openapi.json)
* [https://api.xdataapi.io/openapi.yaml](https://api.xdataapi.io/openapi.yaml)
