Skip to main content
Cal ID limits how many API calls you can make over a given window. Most responses from https://api.cal.id carry headers describing your current allowance, so a well-behaved integration can throttle itself before it is throttled.

Rate-limit headers

These headers are not on every response. A request rejected before it reaches a handler carries none of them — that includes 401 responses, 400 schema-validation failures, and unknown-route 404s. Treat a missing header as “unknown”, never as zero.X-RateLimit-Reset is an ISO-8601 string, so Number(reset) * 1000 yields NaN. Parse it with Date.parse() or your language’s ISO parser, or just use Retry-After, which is already in seconds.

What the limit actually is

Measured against https://api.cal.id in September 2026: 100 requests per rolling 60-second window, with X-RateLimit-Limit: 100 on the responses that carry it. Request 101 returned a 429 with Retry-After: 31.
That number is observed behaviour, not a contractual quota, and it is not tiered by plan as far as testing could determine. Size a batch job against it, but keep the backoff below — do not assume the ceiling is fixed.
Log X-RateLimit-Remaining when it is present. Watching it trend toward zero is the cheapest early warning that a job is calling the API too aggressively.One caveat for browser code: api.cal.id does not send an Access-Control-Expose-Headers response header, so a cross-origin fetch() cannot read any of the rate-limit headers — they will read as null however many the server sent. Read them from a server-side client instead.

When you exceed the limit

Once you run out of allowance, Cal ID responds with HTTP 429 Too Many Requests and a JSON body:
retryAfter is the number of seconds to wait before your next call is allowed.
Do not retry a 429 immediately or in a tight loop. Hammering the endpoint keeps you throttled and delays your own recovery.

Handle limits gracefully

1

Check the status code

Treat 429 as its own case, separate from other errors.
2

Wait for retryAfter

Sleep for the number of seconds in retryAfter, or in the Retry-After header. Both are in seconds. If you prefer X-RateLimit-Reset, parse it as an ISO-8601 timestamp rather than a number.
3

Back off exponentially

If you are still throttled after the wait, double the delay on each subsequent attempt and cap the number of retries.
4

Reduce the call volume

Batch work, cache responses that rarely change (such as event types), and raise the limit parameter on list endpoints instead of paging one record at a time.
The first three steps together make a small wrapper you can put every call through. Both versions log X-RateLimit-Remaining, sleep for retryAfter, double the delay on each attempt, and give up after five tries.
A 429 is genuinely transient, so retrying is correct. A 500 often is not: Cal ID returns 500 for scheduling-rule failures such as no_available_users_found_error, which will fail identically on every retry. (Malformed requests return 400, not 500.) Read the response body before you retry. See API integration for details.

API integration

Base URL, authentication, response envelopes, and common pitfalls.

Get your API key

Use separate keys per integration so you can isolate a noisy one.

Webhooks

Receive booking events as they happen instead of polling the API.

API Reference

Every endpoint, with schemas and a live “Try it” playground.