Error Handling
| Status | Meaning | What to do |
|---|---|---|
400 | Invalid request | Fix the request before retrying |
401 | Missing or invalid credentials | Check the server-side API key |
402 | Insufficient credits | Top up before retrying |
403 | Account or route not allowed | Review account permissions |
404 | Resource not found | Check the URL or resource ID |
409 | Conflict (e.g. idempotency key already in flight) | Wait and retry, or reuse the stored result |
429 | Rate limited | Back off and retry with jitter |
5xx | Temporary service failure | Retry safely with exponential backoff |
Error responses include a structured code, a human message, and a request_id for support investigations.
{
"success": false,
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded"
},
"request_id": "dfffa252-4781-43ff-8e1a-bf01a754d66a"
}Principles
- Do not blindly retry a send request. A send is not inherently idempotent — reuse an
Idempotency-Key(max 128 chars) for the single and bulk endpoints so a retry replays the stored result instead of sending twice. - Treat
429and5xxas safe to retry. Everything else usually needs a code fix, not a retry. - Log the
request_id. When you open a support ticket, include it so the team can trace the exact request. - Match with typed SDK errors rather than string-matching
codevalues:
| SDK | How to catch |
|---|---|
| Go | errors.As(err, *sendafrica.APIError); apiErr.IsRateLimited() |
| Python | except InsufficientCreditsError etc. from sendafrica |
| TypeScript | err instanceof SendAfricaError; err.isRateLimited |
Last updated on