Errors and rate limits
Every error has the same shape:
{ "error": { "code": "insufficient_credits", "message": "Not enough credits. Top up your balance and try again.", "details": { "available": 12, "required": 40 } } }Rely on code (stable), not on message (may change).
| HTTP | code | Meaning | What to do |
|---|---|---|---|
| 400 | validation_error | Invalid body/parameter; details lists fields | Fix the request. Unknown fields are rejected |
| 400 | idempotency_key_required | Missing Idempotency-Key on a send | Add a unique UUID per logical send |
| 401 | unauthorized | Key missing, wrong, expired or revoked | Check the key |
| 402 | insufficient_credits | Not enough credits | Buy a package in the portal |
| 403 | insufficient_scope | The key lacks the scope | Create a key with the right scope |
| 403 | sender_not_verified | The sender name is not approved for your account | Use a name from GET /senders or request one in the portal |
| 403 | forbidden / org_suspended | No access / account suspended | Contact support |
| 404 | not_found | Resource does not exist (or belongs to another account) | — |
| 409 | idempotency_conflict | Same key with a different request, or the original is still processing | Use a new key for a new request |
| 422 | invalid_destination | No valid destination; see details.rejected | Check the numbers |
| 429 | rate_limited / otp_rate_limited / daily_limit_exceeded | Limit exceeded | Wait retry_after seconds |
| 500/503 | internal_error / service_unavailable | Temporary problem | Retry with the same Idempotency-Key |
Delivery failure reasons (reason)
undeliverable, network_failure, expired, kosher_phone (a "kosher" number that blocks SMS), send_window (sending not allowed at this hour), blocked (recipient blocked messages — auto-added to your blacklist), invalid_content, no_coverage, rejected, cancelled, temporary_unavailable, internal_error.
Rejection reasons in a send response (rejected[].reason): invalid_phone, international_not_allowed, blacklisted, recently_messaged, duplicate, body_too_long, empty_body.
Rate limits
| Kind | Limit per key |
|---|---|
| Send (POST) | 60 requests / minute |
| Read (GET) | 600 requests / minute |
| OTP to the same phone | 3 per 10 minutes, 10 per hour |
Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset; a 429 also has Retry-After. For high volume use /messages/batch (up to 2,500 messages per request) instead of one request per message.
New accounts are limited to 500 messages per day until business details are verified.