Skip to content
Keptslot
Start free

Rate limits and quotas

Two different things can refuse your request for reasons of volume, and they behave nothing alike.

A rate limit is about requests per minute. It answers 429 and it clears on its own.

A plan quota is about what the account bought — bookings this billing period, active calendars, workspaces. It answers 402, and it clears when the period rolls over or the plan changes. Quotas are covered in the errors guide; the rest of this page is rate limits.

This is the fact to design against, and it is the one most likely to be assumed wrong.

The limiter counts requests per calendar minute. The counter for the minute starting at 10:04:00 is reset at 10:05:00, all at once, for everybody. It is not a rolling sixty-second window.

Two consequences fall straight out:

You can spend a whole minute’s budget in the last second of a minute, and another whole budget in the first second of the next. Twenty calls in two seconds is legal. That is a property of fixed windows, not a loophole, and we mention it because backoff written for a sliding window over-corrects here.

Backing off by less than a whole minute may not help. If you are refused at 10:04:30, waiting five seconds gets you refused again. Wait until the next minute boundary.

A request that is turned away with a 429 has already consumed one unit of that minute’s budget. The refusal is counted, not waived.

This is deliberate, and it means a client that retries aggressively inside one minute keeps its own budget at zero. Do not retry a 429 inside the same minute. You are not just failing; you are paying for each failure.

Rate limits apply to publishable keys. A secret key is not rate-limited.

Endpoint Requests per minute
GET /v1/availability 60
POST /v1/bookings 10
POST /v1/bookings/{id}/verify 10

Each of those is charged twice over: once against the key, and once against the client IP, with the same number. Both counters increment, so a request that clears the key’s budget and then trips the IP budget has still spent the key’s unit. The IP axis exists so that many keys behind one address cannot multiply the effective budget.

The keyless customer routes under /v1/public — the bootstrap call and the manage link’s cancel and reschedule — get 30 requests per minute per IP, shared across all of them. They carry no key, so an IP is the only subject available.

We do not send Retry-After, and we do not send X-RateLimit-Remaining or anything like it. A 429 is a JSON body and nothing else:

{
"error": {
"type": "rate_limited",
"message": "Too many requests."
}
}

So you cannot read your remaining budget off a response. Pace against the table above, and treat the next minute boundary as the retry time.

Idempotency is the other half of retrying safely

Section titled “Idempotency is the other half of retrying safely”

POST /v1/bookings accepts an Idempotency-Key header. It is the mechanism that makes a retry safe, and it is worth using on every create rather than only in your error path.

Send a fresh unique value per booking attempt — a UUID is ideal:

Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7

A repeat with the same key and the same body replays the stored response, including its original status code. You get the same booking id back, and no second appointment is created. This is what saves you when a request times out after the booking was actually made.

A repeat with the same key and a different body is refused with a 422 and idempotency_mismatch. The stored response is never replayed in that case: reusing a key with different content is always a client bug, and replaying would hide it.

Two details worth knowing:

  • Only a 2xx is stored. A failed create is not remembered, so you are free to retry it cleanly with the same key.
  • Simultaneous requests do not replay. Two identical creates arriving at the same instant cannot both take the slot, but the loser gets its own 409 rather than a copy of the winner’s response. Replay is guaranteed for the sequential case, which is the one a timeout produces.

Retry a 429 on the next minute boundary. Retry a 503 — it means two writes contended and the request is expected to succeed — after a short backoff. Retry a create with the same Idempotency-Key you used the first time. Do not retry any other 4xx: the same request will get the same answer.