Skip to content

Caching

Successful responses are cached for 60 seconds, on the server, per token and per URL.

This is a shared cache in front of the API, not a hint to your HTTP client. A cached response is returned without the request reaching authentication, the services, or the database.

The cache key

Two things identify a cached response:

  1. Your token — strictly, a hash of the Authorization header.
  2. The full request URL, including the query string.

Because the token is part of the key, responses are never shared between tokens. A request from a different project cannot be served another project’s cached data.

Because the query string is part of the key, ?page=1 and ?page=2 are separate entries — and so are ?page=1&page_size=20 and ?page_size=20&page=1, which differ as strings even though they mean the same thing.

Reading the cache status

Every response says whether it came from the cache:

HTTP/1.1 200 OK
X-Localeo-Cache: hit
Cache-Control: public, max-age=60
X-Localeo-CacheMeaning
missHandled normally, and the response has now been stored.
hitServed from the cache without touching the database.
unreachableNeither read from nor written to the cache.

unreachable is the one that needs explaining, because it does not only mean the cache is down. Only successful responses are cacheable, so every error response reports unreachable — a 401 or a 404 is never stored, and you will see this value on it routinely. It also appears if the cache backend is unavailable, which is not an error either: the cache is an optimisation, and the API deliberately keeps serving requests without it.

In short, treat unreachable as “this response did not involve the cache”, and only hit as a guarantee that no database work happened.

Successful responses also carry Cache-Control: public, max-age=60, so your own HTTP client or CDN may cache them for the same minute.

Consequences worth knowing

A revoked token keeps working briefly

Because a cache hit never reaches authentication, deleting a token does not immediately stop responses to requests that match something already cached. The gap is bounded by the 60-second lifetime.

Fresh data can be up to a minute old

A release published seconds ago may not appear in a GET /releases response you have already made within the last minute. If you need to observe a publish immediately, vary the query string — adding an ignored parameter such as ?t=1743158400 produces a different cache key and forces a fresh read.

Use that sparingly. Every forced miss is a database query and, unlike a cache hit, it counts against your rate limit.

Cache hits are free

A cache hit is not counted against your rate limit, because the cache sits in front of the limiter. Repeatedly requesting the same URL is much cheaper than the request count implies — polling a stable URL once a second for a minute costs one unit of quota, not sixty.

What is not covered by this cache

Translation bundle downloads. Those are static files on the CDN with their own, much longer cache policy — a pinned release is immutable and cached for a year. That is described in Releases & bundles.