Rate limits

Requests are limited to 5 per second, counted per token. The limit is generous for typical use — batch or back off if you need to pull large amounts of data.

Rate-limit headers

Every response includes headers describing your current budget:

Header Description
X-RateLimit-Limit The maximum number of requests allowed in the window.
X-RateLimit-Remaining Requests remaining in the current window.

When you exceed the limit, the API responds with 429 Too Many Requests and adds:

Header Description
Retry-After Seconds to wait before retrying.
X-RateLimit-Reset Unix timestamp when the window resets.
{
    "message": "Too Many Attempts."
}

Example — retry politely after a 429

Read Retry-After and pause before retrying; add a little jitter so concurrent clients don't stampede back at the same instant:

async function getWithRetry(url, options, attempts = 3) {
    for (let attempt = 1; attempt <= attempts; attempt++) {
        const response = await fetch(url, options);

        if (response.status !== 429) return response;

        const retryAfter = Number(response.headers.get('Retry-After') ?? 1);
        const jitter = Math.random() * 250;
        await new Promise((resolve) =>
            setTimeout(resolve, retryAfter * 1000 + jitter),
        );
    }

    throw new Error('Rate limited after retries');
}

Avoid hitting the limit at all

  • Cache responses. meta.tzdb_version tells you when the underlying data actually changed — cached reference data (zones, countries, abbreviations) stays valid until the next tzdb release.
  • Prefer one filtered list call over many lookups. GET /v1/timezones?country=us is one request; fetching 29 zones one by one is 29.
  • Spread scheduled jobs out rather than firing them on the same second.