Error Handling
Requests return typed errors. The core type is *sendafrica.APIError:
if err != nil {
var apiErr *sendafrica.APIError
if errors.As(err, &apiErr) {
fmt.Println(apiErr.Code, apiErr.Message, apiErr.RequestID, apiErr.StatusCode)
switch {
case apiErr.IsRateLimited(): // 429 / rate_limit_exceeded
case apiErr.IsInsufficientCredits(): // 402 / insufficient_credits
case apiErr.IsUnauthorized(): // 401 / invalid_api_key
}
}
if errors.Is(err, sendafrica.ErrInvalidPhone) {
// local phone validation failed
}
}What APIError carries
StatusCode, Code, Message, RequestID, RetryAfter, Body, and Headers. Log the RequestID and include it in support tickets.
Sentinel errors
ErrInvalidPhone and ErrMissingAPIKey are sentinel errors you can match with errors.Is. Network-level transport failures are wrapped with sendafrica: request failed: ....
The happy path returns a typed result
Methods return (Result, error) — on success the typed result fields are ready to use; any failure is nil result plus error. NewClient returns an empty client only when retries are exhausted into a persistent transport error.
Retries & backoff
The client retries 429 and 5xx responses (and network failures) up to WithMaxRetries times (default 3) using exponential backoff from 500 ms up to a maximum of 8 s, honoring the API’s Retry-After header when present. Each attempt also sends a fresh X-Request-Id header.
See Error Handling for the status-code reference and send-safe retry guidance.