# 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 `429` with `Retry-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 `429` until 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](../../examples/cli/) does
it for you.

To stay far below the limit: poll a run every 10 to 30 seconds (not every second), use
[webhooks](webhooks.md) for long runs, and use `after_seq` when following a call trace.

## Pagination

List endpoints that can grow return a page:

```json
{ "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](ci-cd.md).

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](reference/scenario-grammar.md):

| 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.
