API stability policy
Version 1.1, effective 5 August 2026
If we have to break something in /api/v1, you get twelve months of notice and the old behaviour keeps working throughout. We are not promising never to break anything, because that promise is worthless the first time it is broken. We are promising you will not find out from a stack trace.
Changes to this policy are themselves announced on the changelog, with the version above bumped, so it cannot be quietly rewritten under you.
What counts as a breaking change
These changes get the full twelve-month notice period:
- Removing an endpoint, or changing its URL.
- Removing a field from a response, or renaming one.
- Changing the type of an existing field, including making a number a string or a string null.
- Changing the meaning of an existing value, such as redefining what a status enum member represents.
- Removing a value from an enum that responses already return.
- Making an optional request parameter required.
- Tightening validation so a request that worked yesterday now returns 400.
- Lowering a published rate limit on a plan somebody is already paying for.
What we may change at any time
These are not breaking changes, and your client should be built to tolerate them. Saying so up front is more useful than a promise that quietly excludes them later:
- Adding a new endpoint.
- Adding a new field to a response. Parse permissively: do not fail on fields you do not recognise.
- Adding a new optional request parameter.
- Adding a new value to an enum, when the data itself gains a new category. Handle the unknown case.
- Changing the order of items in a response where no order is documented.
- Fixing a value that was wrong in a single record. A corrected operator name or a status that was misread for one satellite is a data fix, not an API change.
- Changing rate limits on the anonymous and free tiers, up or down, with notice on the changelog.
- Performance, caching, and the exact wording of error messages. Match on the status code and the code field, never on prose.
The data-fix line has a limit, because without one it would swallow the whole policy. A correction that changes how a field is derived for the catalog as a whole, or that moves more than one percent of records, gets a changelog entry with an effective date even though it is not a twelve-month breaking change. That has already happened once: on 3 August 2026 operational_status stopped being derived from GCAT, which moved most of the catalog and shipped with no notice at all. It is the worked example of the change this paragraph now covers, and it is written up on the changelog.
The stable identifier is the code field in the error body. error and hint are human copy and their wording may change at any time, so do not branch on them. Today code is on every 401, 429 and 503 the shared API guard produces, and on the 400 and 500 from /satellites. The remaining routes still return only error and hint; adding code to them is additive and is in progress. The list of possible values is published in openapi.json. Adding a value to it is additive; renaming one is a breaking change.
How a deprecation actually runs
- It is announced on the changelog, and by email to every account with an API key that has been used in the previous ninety days.
- Affected responses start carrying a Deprecation header and a Sunset header with the removal date, so an automated client can notice without anybody reading an email.
- The old behaviour keeps working, unchanged, for at least twelve months from that announcement.
- A month before the date, we contact remaining callers again if the endpoint is still being used.
- On the date, the old behaviour is removed.
No endpoint sends Deprecation or Sunsettoday, because nothing is deprecated. Both header names are already on the API’s Access-Control-Expose-Headers list, so a browser client will be able to read them the day they first appear rather than the day we remember to add them.
Versioning is in the path, so /api/v1 is a promise about that prefix specifically. A change we cannot make compatibly becomes /api/v2 alongside it, not a redefinition of v1.
Where this policy does not reach
Three honest exceptions, because a policy with hidden exceptions is not a policy.
- Security. If a field or endpoint is leaking something it should not, it changes immediately and we explain afterwards.
- Upstream sources. The data comes from CelesTrak, GCAT, Wikidata and SatNOGS DB, and if one of them changes what it publishes or withdraws it, we cannot hold a field open that no longer has anything behind it. We would tell you as soon as we knew, and the changelog is where it would appear.
- The Terms of Service. During the prototype phase the Terms govern, and sections 2, 9 and 12 reserve the right to change or withdraw features, endpoints, response shapes, limits and availability without notice. This page is how we intend to behave and how we expect to be held to account, but it is not a contractual override, and where the two conflict the Terms win. If you need a contractual commitment rather than a stated intent, ask us for one in writing before you build.
This is also why the API is read-only, and why reads work with no key at all, at a small shared allowance of 50 requests a day per network. That anonymous allowance is not the Free tier: the Free tier is a keyed plan, and a free account key raises you to 1,000 a day. The fewer promises we make, the more of them we can keep.