PLATFORM
Errors & limits
The API uses conventional HTTP status codes and returns a consistent error body with a machine-readable code, so you can branch on failures reliably instead of parsing prose.
The error object
{
"error": {
"type": "invalid_request",
"code": "invalid_phone_number",
"message": "The 'to' value is not a valid E.164 phone number.",
"param": "to",
"request_id": "req_01HN9Z..."
}
}
| Field | Description |
|---|---|
type | Broad category: invalid_request, authentication, rate_limit, api_error. |
code | Stable, specific identifier to branch on in code. |
message | Human-readable explanation. For logs and debugging, not for end users. |
param | The offending field, when applicable. |
request_id | Matches the X-Request-Id header. Quote it to support. |
HTTP status codes
| Code | Meaning |
|---|---|
200 / 201 | Success. 201 when a resource was created. |
400 | Malformed request — invalid JSON or missing body. |
401 | Missing or invalid API key. |
403 | Authenticated, but not allowed to do this. |
404 | No such resource. |
409 | Conflict — e.g. an idempotency key reused with a different payload. |
422 | Valid JSON but a field failed validation (see param). |
429 | Rate limited. Back off and retry (see below). |
500 / 503 | Something went wrong on our side, or a temporary outage. Safe to retry with backoff. |
Common error codes
| Code | Meaning |
|---|---|
invalid_phone_number | A number wasn't valid E.164. |
unsupported_route | No route to that destination for the channel/number type. |
sender_not_allowed | The from isn't permitted in the destination market. |
recipient_opted_out | The recipient previously sent STOP; the send was blocked. |
domain_not_verified | Email from domain isn't verified for a live key. |
rate_limited | Too many requests; retry after the window. |
insufficient_balance | Account balance can't cover the request. |
Rate limits
Limits are applied per API key and per endpoint group. Every response includes your current standing so you can pace yourself before hitting a wall:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1782475260
Exceeding a limit returns 429 with a Retry-After header (seconds). If you expect sustained high throughput — a campaign or a seasonal peak — arrange dedicated throughput ahead of time rather than relying on burst headroom.
Retries & idempotency
- Retry
429,500, and503with exponential backoff and jitter. Don't retry400,401,403,404, or422— fix the request instead. - Always send an
Idempotency-Keyon writes so a retry after a timeout can't create a duplicate message, call, or charge. - Treat webhook delivery as at-least-once and de-duplicate on the event
id.