Rate limits

Standard headers on every response.

Defaults

Route classLimitWindowKeyed by
Authenticated /v1/pulse/*600 requests / minute60sAPI key
Anonymous (/v1/pulse widget, /v1/health, /v1/status)60 requests / minute60sIP address

Need a higher limit? Contact us. For real integrations we’ll bump you up.

Response headers

Every response includes:

X-RateLimit-Limit: 600
X-RateLimit-Remaining: 598
X-RateLimit-Reset: 1788981735
  • X-RateLimit-Limit is the ceiling for the current window.
  • X-RateLimit-Remaining is how many calls you have left before you hit 429.
  • X-RateLimit-Reset is the unix-seconds timestamp when the counter resets.

When you’re rate-limited

You get HTTP 429 with a Retry-After header (seconds) and this envelope:

{
"error": {
"type": "rate_limit_error",
"code": "rate_limited",
"message": "Too many requests. Try again in 42s.",
"request_id": "req_..."
}
}

Back off for at least Retry-After seconds. Don’t retry aggressively, or you’ll just extend the window.

  • Cache at the property level. For a given (lat, lng, start, end) tuple, Chora responses are stable within meta.fresh_until. Cache by that key rather than hitting the API on every page render.
  • Prefer the composite widget for anonymous embeds. If you don’t need JSON in your app, /v1/pulse handles a whole booking page in one anonymous HTML request.
  • Request sections in parallel. Every JSON endpoint is independent. Promise.all your weather + astronomy + michelin + events calls.
  • Read the Reset header. After a 429, wait until the header’s unix time before retrying.