#Rate Limiting
The AiDial API enforces application-level and infrastructure-level rate limits to protect service availability. When a request is rejected by a rate limiter, the API returns a 429 Too Many Requests response.
#Detecting Rate Limits
A 429 HTTP status code indicates you have been rate-limited. Before parsing the response body, check the Content-Type header and retry signals:
- JSON body (
application/json): Parse the response for error details and retry timing - Non-JSON body (
text/plainortext/html): Infrastructure-level rate limit; do not attempt to parse as JSON - Retry timing: Prefer the
Retry-Afterresponse header when present; otherwise use a documented retry field in the JSON body, such asretry_after_seconds
#Current Application and Edge Limits
| Surface | Current limit | Retry signal |
|---|---|---|
| Edge proxy main API router | Traefik average 1,200 requests per 1-minute period with a burst of 300 | No JSON contract guaranteed |
| Partner API key and webhook operations | 5 requests per operation per 60 seconds. API-key create/rotate scope by authenticated identity, target tenant, and data environment; webhook create/rotate-secret/test scope by authenticated identity and target tenant | Retry-After header |
| Knowledge base build and preview | 12 build/rebuild requests per authenticated identity and tenant per 60 minutes; 30 preview queries per authenticated identity and tenant per 300 seconds | Retry-After header |
| CSV export | 3 requests per user/tenant pair, and 12 requests per tenant, per 600 seconds | retry_after_seconds body |
| Transcript downloads | 10 requests per user/tenant pair, and 60 requests per tenant, per 300 seconds across TXT and PDF downloads | retry_after_seconds body |
| SCIM security endpoints | 30 configuration updates per authenticated identity, tenant, data environment, and operation per 60 seconds; 120 provisioning events per scoped API-key identity, tenant, and data environment per 60 seconds | Retry-After header |
| SSO security endpoints | 20 configuration/role-mapping mutations per authenticated identity, tenant, data environment, and operation per 60 seconds; 10 break-glass operations per authenticated identity, tenant, data environment, and operation per 60 seconds | Retry-After header |
| Communication email routing | Add email: 10 per user/tenant per 24 hours; resend verification: 3 per user/email/tenant per 1 hour; public token verification: 10 per source IP per 60 seconds | Retry-After header and retry_after_seconds body |
| Account export jobs | 3 create requests per user/tenant/data environment, and 10 per tenant/data environment, per 24 hours | Retry-After header |
| Report schedule sample sends | 5 per actor/tenant per 600 seconds, and 3 per tenant/frequency/recipient-set per 900 seconds | Retry-After header and details.retry_after_seconds body |
| Business-hours project options | 20 requests per authenticated actor per 60 seconds | field_errors.retry_after_seconds body |
| Portal MFA lifecycle mutation | 5 requests per client, user, and action per 60 seconds | Retry-After header and retry_after_seconds body |
| Compliance PII unlock requests | 10 unlock requests per actor, tenant, data environment, and call per 15 minutes | No retry timing |
| Support portal surfaces | Route-local per-actor/operation limits over 60 seconds: ticket search 30, badge/open-count 60, ticket comment read 60, related-call validation 60, related-call attach/detach 20, recipient add/remove 20, attachment readiness/list/scan-status 60, attachment finalise/download 30, attachment session-start/delete 12 | No retry timing |
| Public demo dial-me | Current configured limits are 5 per phone per hour, 10 per phone per day, and 10 per resolved public source IP per day; active call and runtime capacity blocks also return 429 | No retry timing |
| Authenticated DialMe dispatch | No request-count limit in the API. POST /v1/dialme/dispatch returns 429 only when the AiDial runtime reports a capacity block | No retry timing |
#Response Formats
For public demo dial-me IP limiting, the source IP is resolved through the trusted request-provenance helper before counters are updated. Spoofed forwarded headers from untrusted direct peers are ignored, while malformed trusted proxy chains or chains that resolve only to internal/trusted infrastructure are rejected with 400 before dispatch.
#General HTTPException-Based Routes
Some routes return the general error shape from the global FastAPI exception handler:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json{
"error": "Rate limit exceeded",
"status_code": 429
}Public demo dial-me, authenticated DialMe dispatch, and Compliance PII unlock throttling use this plain-string form; the error value carries the route's message (for example, Too many PII unlock requests. Please retry shortly. or DialMe call capacity reached). These responses do not guarantee retry timing.
When an HTTPException carries a structured detail object, the global handler wraps that object in error. The Support portal surfaces use this form, raising a 429 with a rate_limit_exceeded code and an operation-specific message and no retry timing:
{
"error": {
"code": "rate_limit_exceeded",
"message": "Too many requests."
},
"status_code": 429
}#Partner Endpoints
Partner endpoints using the shared rate limiter return the partner error envelope and include a Retry-After response header:
HTTP/1.1 429 Too Many Requests
Retry-After: 3
Content-Type: application/json{
"error": {
"code": "rate_limited",
"message": "Too many requests. Please retry shortly.",
"message_key": "rate_limited",
"status": 429,
"request_id": "9b603a24-4d95-4d0b-a865-20c0f8a2c5de"
}
}Knowledge base build/rebuild and preview rate limits use this same partner envelope and message text with code set to rate_limited.
SCIM and SSO security rate limits use the same partner envelope with code set to rate_limited; the SCIM message text is SCIM-specific.
#Billing Endpoints
Billing 429 responses use the billing domain error shape. When the billing provider supplies retry timing, AiDial includes a Retry-After response header with the number of seconds to wait:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json{
"code": "billing_provider_rate_limited",
"message": "Billing provider rate limit exceeded."
}#Export and Transcript Download Endpoints
POST /v1/calls/export, GET /v1/calls/{call_id}/transcript-download, and GET /v1/calls/{call_id}/transcript/pdf return export/download error bodies. Their 429 responses include retry_after_seconds in the response body:
{
"error": "rate_limited",
"code": "EXPORT_THROTTLED",
"message": "Export throttled. Please wait and try again.",
"messageKey": "calls.export.messages.throttled",
"retry_after_seconds": 60
}Transcript TXT and PDF downloads share the same download limiter and use DOWNLOAD_THROTTLED:
{
"error": "rate_limited",
"code": "DOWNLOAD_THROTTLED",
"message": "Transcript download throttled. Please wait and try again.",
"messageKey": "calls.transcriptDownload.messages.throttled",
"retry_after_seconds": 9
}Concurrent CSV exports for the same user and tenant return 409 Conflict with EXPORT_IN_PROGRESS, not 429.
#Portal Domain Endpoints
Several portal-facing domain endpoints return a flat domain error shape.
Communication email routing uses code: "rate_limited" and includes both Retry-After and retry_after_seconds:
{
"code": "rate_limited",
"message": "Request rate limit exceeded.",
"retry_after_seconds": 60
}Account export job creation uses code: "export_rate_limited" and includes Retry-After:
{
"code": "export_rate_limited",
"message": "Account export request limit reached for this user."
}Report schedule sample sends include retry timing under details and set Retry-After:
{
"code": "rate_limited",
"message": "Too many requests. Please try again later.",
"details": {
"reason": "actor",
"retry_after_seconds": 600
}
}Business-hours project options use the domain error shape and put retry timing in field_errors:
{
"code": "business_hours_options_rate_limited",
"message": "Request rate limit exceeded.",
"field_errors": {
"retry_after_seconds": "60"
}
}#Portal MFA Lifecycle
PUT /v1/portal-mfa-lifecycle applies a route-local limiter per client, user, and action. Its 429 response uses the domain error shape and includes both Retry-After and retry_after_seconds:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json{
"code": "rate_limited",
"message": "Too many requests. Please wait and try again.",
"retry_after_seconds": 60
}#Infrastructure-Level Rate Limiting
Infrastructure-level rate limiting is enforced by Traefik for the main API router with an average of 1,200 requests per minute and a burst of 300. It may return a 429 response with a non-JSON body (plain text or HTML). Your integration must handle this gracefully:
HTTP/1.1 429 Too Many Requests
Content-Type: text/plain
Rate limit exceededThe dedicated /v1/sms/telnyx/inbound edge router applies the same API rate-limit middleware plus an additional shared platform rate limiter, so carrier webhook traffic can be throttled at the edge before it reaches the application.
#Recommended Retry Strategy
Use exponential backoff with jitter:
- Check
Content-Typebefore parsing the 429 body - If
Retry-Afterheader is present: Wait the specified number of seconds - If JSON with documented retry timing: Use
retry_after_seconds,details.retry_after_seconds, orfield_errors.retry_after_secondswhere documented for that endpoint - If non-JSON or no retry timing: Wait a minimum of 60 seconds
- On subsequent retries: Double the wait time (60s, 120s, 240s) up to a maximum of 10 minutes
- Add jitter: Add a random delay of 0-5 seconds to avoid thundering herd effects
#Related Pages
- Error Responses — all error formats and status codes
- Authentication — API key and bearer authentication