Rate limits and concurrency
Two limits apply to each organization. Both are shared by all of the organization’s keys.
| Limit | Default | What it counts |
|---|---|---|
| Requests per minute | 30 | Every authenticated request to the API, in fixed 60 second windows. |
| Concurrent live calls | 2 | Calls placed with POST /calls that are queued, dialing or in_progress. |
These are the defaults. If you need higher limits, email sales@sahay.io.
Requests per minute
Section titled “Requests per minute”The window is fixed, not sliding: it lasts 60 seconds from its first request, and the count resets when it ends. A request that carries a valid key counts against the limit even if it later fails. Requests with a missing, malformed, unknown or revoked key are not counted.
When you go over the limit, the API answers 429 with the code rate_limited and a Retry-After header:
{ "error": { "code": "rate_limited", "message": "Rate limit of 30 requests per 60s exceeded." }}Wait Retry-After seconds before you try again.
Rate limit headers
Section titled “Rate limit headers”Successful responses carry these headers. The three X-RateLimit headers are also on a 429.
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Requests allowed per window. |
X-RateLimit-Remaining |
Requests left in the current window. |
X-RateLimit-Reset |
When the window resets, as a Unix timestamp in seconds. |
Retry-After |
On a 429 rate_limited only: seconds to wait. |
Concurrent live calls
Section titled “Concurrent live calls”POST /calls is refused with 429 live_call_limit when your organization already has its maximum number of API calls in progress. This response has no Retry-After header. Retry when a call has ended, which you can detect with the call.completed webhook or by reading the call.
Notes:
- Only calls placed through
POST /callscount. Campaign calls do not use this limit. - A call that has stayed
queued,dialingorin_progressfor more than 60 minutes no longer holds a slot.
Staying inside the limits
Section titled “Staying inside the limits”- Use webhooks instead of polling
GET /calls/{call_id}. - Page lists with
limitandcursorrather than requesting many small pages. - Add contacts in batches of up to 500 per request.
- Back off on
429and honorRetry-After.