Skip to content
x.md
Esc
navigateopen⌘Jpreview
On this page

Versioning and deprecation

What /api/v1 promises, how a change is announced, and how long a route keeps working after it is deprecated.

x.md is meant to be integrated once and then left running. This page is the promise about when that integration can break, and how much warning it gets.

Two surfaces

Surface Example Contract
Versioned machine surface GET /api/v1/search?q=vercel v1 is the current major version. Breaking changes ship as /api/v2/*, never inside v1.
Permalink product surface GET /search?q=vercel Mirrors the shape of an x.com URL. Unversioned, stable, not deprecated.

The two surfaces run the same code and return the same bodies. Pick the versioned paths when you are writing a client that has to keep working unattended; pick the permalink paths when you are rewriting an x.com URL you already hold.

The versioned surface is:

GET /api/v1/posts                              # ?url= or ?handle= + ?id=
GET /api/v1/profiles/{handle}
GET /api/v1/profiles/{handle}/followers
GET /api/v1/profiles/{handle}/following
GET /api/v1/search                             # ?q=
GET /api/v1/oembed                             # ?url=

What can change inside v1

These are additive and ship without a version bump:

  • New optional query parameters.
  • New fields in a JSON response body, and new keys inside existing objects.
  • New response headers.
  • New values in an open-ended field such as source.
  • Changes to the rendered Markdown: wording, ordering, and layout are presentation, not contract.

Ignore JSON fields you do not recognise, and do not parse the Markdown as if it were a data format. If you need fields, ask for format=json or send Accept: application/json.

These are breaking, and ship only under a new path prefix:

  • Removing or renaming a query parameter, a JSON field, or a route.
  • Changing the type of an existing field, or narrowing what an existing parameter accepts.
  • Changing the meaning of an existing status code or error code.

/api/v1/* keeps working for at least 12 months after its successor is published.

How a deprecation is announced

A deprecated route keeps answering exactly as before and adds three signals to every response it produces, success and error alike:

Deprecation: @1789430400
Sunset: Wed, 15 Sep 2027 00:00:00 GMT
Link: <https://x.pcstyle.dev/api/v1/posts>; rel="successor-version", <https://x.pcstyle.dev/docs/versioning>; rel="deprecation"; type="text/html", <https://x.pcstyle.dev/docs/versioning>; rel="sunset"; type="text/html"
Header Specification How to read it
Deprecation RFC 9745 A structured-field Date: @ followed by Unix seconds. @1789430400 is 2026-09-15T00:00:00Z. A date in the future means the route becomes deprecated then.
Sunset RFC 8594 An HTTP-date after which the route may stop answering. Never earlier than the Deprecation date.
Link; rel="successor-version" RFC 8288 The route to move to. Safe to follow automatically.
Link; rel="deprecation" RFC 9745 This page.
Link; rel="sunset" RFC 8594 This page.

The /api/* routes list Deprecation, Sunset, and Link in Access-Control-Expose-Headers, so a browser client can read them cross-origin.

Scheduled for deprecation

Route Successor Deprecation date Sunset
GET /api/convert GET /api/v1/posts 2026-09-15 2027-09-15
GET /api/browse GET /api/v1/profiles/{handle}, /followers, /following, GET /api/v1/search 2026-09-15 2027-09-15

Both aliases send Deprecation and Sunset on every response now, before the date the Deprecation field names. That is the point of a future date: it is notice that the route becomes deprecated on 2026-09-15, not a claim that it already is.

/api/browse selects its successor by resource: profile, followers, and following map to the matching /api/v1/profiles/{handle} route, and search maps to /api/v1/search. A call that names no resolvable resource still carries Deprecation and Sunset, but no successor-version link.

Both aliases keep working unchanged until the sunset date. Nothing else is deprecated. The permalink routes are not aliases and are not going away.

Check it from code

Deprecation is visible on a HEAD, so a client can check without spending a read:

curl -sSI 'https://x.pcstyle.dev/api/convert?url=https://x.com/jack/status/20' \
  | grep -iE '^(deprecation|sunset|link):'

A reasonable client policy: log a warning when Deprecation is present, follow rel="successor-version" on the next deploy, and fail loudly if Sunset is less than 30 days away.

The same policy is machine-readable in two places:

  • /openapi.json — the x-api-lifecycle object carries current_major_version, supported_versions, minimum_support_window_months, deprecation_signals, and every deprecated operation with its dates and its replacement: successor when one route replaces the alias, successor_parameter plus successors for /api/browse, which picks its replacement from resource. The operations themselves carry x-successor-version (/api/convert) or x-successor-version-map (/api/browse), alongside OpenAPI’s own deprecated: true.
  • /api — the versioning object repeats the same dates in a smaller document, for an agent that has only the domain. Its /api/browse entry carries successor_parameter and successors rather than a single successor string, so parse both shapes.

Release notes

Changes are tagged on main and listed in CHANGELOG.md. The hosted API at x.pcstyle.dev runs the latest tag.

x.md is a free, MIT-licensed, best-effort project. This policy describes how change is signalled; it is not an SLA. If a route is broken by an upstream provider rather than by a release, the failure shows up as a 502 or 503 problem document, not as a version bump.

Last updated on September 8, 2026

Was this page helpful?