Rate limits and other limits
API rate limits
| Limit | Value | Scope |
|---|---|---|
| Requests | 120 per minute | per API key |
| Failed authentications | 30 per minute | per client IP address |
- Windows are fixed, aligned on the minute.
- Over the limit, the API answers
429withRetry-After(seconds until the window ends) and{"detail": "rate limit exceeded", "code": "rate_limited"}. - Failed authentications are counted per client IP address, apart from everything else. Over
30 in a minute, requests from that IP with an invalid key get
429until the minute ends. Requests with a valid key from the same IP are not affected: they only count against their key's own limit. A CI job with a wrong key cannot lock out the other jobs behind the same NAT.
Handle 429 by waiting Retry-After seconds and retrying. The CLI does
it for you.
To stay far below the limit: poll a run every 10 to 30 seconds (not every second), use
webhooks for long runs, and use after_seq when following a call trace.
Pagination
List endpoints that can grow return a page:
{ "count": 1234, "limit": 50, "offset": 0, "items": [ ] }
| Endpoint | Default limit |
|---|---|
GET /runs, GET /calls, GET /versions, GET /wallet/transactions, GET /webhooks/{id}/deliveries, GET /audit-log |
50 |
GET /exports |
20 |
GET /api-logs |
100 |
GET /calls/{id}/events |
200 |
limit is capped at 200. Items are newest first, except events (by seq). Iterate with
offset until offset >= count.
Scenarios, targets, tags, environments, members, variants, webhooks, alert channels, plans and API keys are returned as plain lists.
CI wait
POST /scenarios/{id}/runs?wait=true holds the request for at most 900 seconds (timeout,
default 600). The server waits asynchronously (it checks the run every 2 seconds without
holding a worker or a database connection), so long waits are fine on our side; proxies on
your side may still cut long requests. See CI/CD.
Sending a test alert (POST /alert-channels/{id}/test) is limited to 5 per minute per
channel.
Plan limits
| Limit | Starter | Growth | Scale |
|---|---|---|---|
| Test calls per month (units) | 100 | 750 | 3,000 |
| Concurrent calls | 2 | 5 | 15 |
| Team seats | 3 | 10 | unlimited |
| Phone countries | 1 | 5 | all |
| Calls per hour to the same number | 30 | 30 | 30 |
| Maximum call duration | 10 min | 10 min | 10 min |
Calls over the concurrency or hourly limits wait in the queue; they are not refused.
GET /me returns your plan's limits (plan.max_concurrent_calls, plan.max_seats,
plan.max_countries, plan.included_calls_per_month).
Scenario limits
From the grammar:
| Field | Range | Default |
|---|---|---|
limits.max_duration_s |
1 to 1800 (then capped by the plan) | 300 |
limits.silence_timeout_s |
1 to 300 | 20 |
limits.ring_timeout_s |
1 to 120 | 45 |
persona.speaking_rate |
0.5 to 2.0 | 1.0 |
steps[].timeout_s (expect) |
up to 300 | 15 |
steps[].seconds (wait) |
up to 600 |
Other sizes: scenario name 200 characters, tag name 40, version label 60, API key name 80, target name 120.