Versioning
How the Improve API is versioned, what counts as a breaking change, and how a deprecation is announced.
The public API is the datafile (GET /config/**) and the analytics endpoint (POST /api/log). It is version 1. Every response from it carries the version in a header:
Api-Version: 1The other /api/* routes belong to the dashboard. They are not versioned and can change at any time, so don't integrate against them.
Stable paths
The current paths have no version prefix, and they are version 1. A breaking change ships under a new path prefix (/v2/config/...) next to the current paths, never in place. Both run side by side until the old version's sunset date.
What can change without notice
Additive changes can ship at any time and are not breaking:
- new endpoints
- new optional request fields
- new response fields, headers or error codes
- new values for
paramsand other open-ended fields
Clients should ignore fields and headers they don't recognize, and treat an unknown error code like its HTTP status.
What counts as breaking
Removing or renaming an endpoint, a field or a header, making an optional field required, or changing the meaning or type of an existing field. These only ship in a new version.
Deprecation
When an endpoint is going away, its responses start sending two headers, and its operation is marked deprecated in the OpenAPI spec:
| Header | Standard | Value |
|---|---|---|
Deprecation | RFC 9745 | When the endpoint was deprecated, e.g. @1767225600 |
Sunset | RFC 8594 | When it stops working, e.g. Thu, 01 Jul 2027 00:00:00 GMT |
Link | RFC 8288 | rel="deprecation" pointing at the migration guide |
The sunset date is at least 6 months after the deprecation date. Deprecations are also listed in the change log.
Browser clients can read all of these cross-origin: they are listed in Access-Control-Expose-Headers.
Rate limit headers
The datafile's browser (Origin) path follows the IETF RateLimit header fields draft:
RateLimit-Policy: "browser";q=600;w=60Every browser response says the quota (q, requests) per window (w, seconds) per IP. A 429 adds the current state and when to retry:
RateLimit: "browser";r=0;t=60
Retry-After: 60Successful responses don't carry RateLimit: they are cached at the edge for 30 seconds and shared between clients, so a per-IP count on them would be wrong for most readers. Server requests with a token header are not rate limited and get no rate limit headers.