Errors

talks uses standard HTTP status codes and returns the same error object every time, so you can handle failures in one place.

Status codes

200 · 201SuccessRequest worked. 201 means something was created.
204No contentDeleted or cancelled. There is no body.
207Partly workedA bulk request where some items failed. Check the failed list.
400Bad requestThe body is not valid JSON or is missing. Fix and resend.
401UnauthenticatedMissing, wrong or revoked API key.
402Out of creditsNot enough credits for this send. Top up, then retry.
403Not allowedYour key or role can not do this, or the sender ID is not approved.
404Not foundThe ID does not exist on your account.
409ConflictAlready exists, or not in a state that allows this action.
422Validation failedA field is invalid. param tells you which one.
429Too many requestsYou hit a rate limit. Wait and retry.
500 · 503Server errorSomething went wrong on our side. Retry with backoff.

The error object

Every error response has a single error object. Log the request_id: support can find your exact request with it.

Error response · 422
{
  "error": {
    "type": "validation_error",
    "code": "invalid_phone_number",
    "message": "to must be in international format, e.g. +254712345678",
    "param": "to",
    "request_id": "req_9Fh2kLm4qR",
    "doc_url": "https://talks.africa/docs/errors#invalid_phone_number"
  }
}
typeThe kind of error. Branch your code on this.
codeA specific, stable reason, such as invalid_phone_number.
messageA human-readable explanation. Do not parse it: it can change.
paramThe field that caused it, when there is one.
request_idUnique ID for the request. Include it when you contact support.
doc_urlLink to the docs for this error.

Error types

authentication_error401No. Fix the key.
permission_error403No. Check roles and sender IDs.
validation_error400 · 422No. Fix the request.
insufficient_credits402After topping up.
not_found404No.
conflict409No. Re-read the resource first.
rate_limit_error429Yes, after Retry-After.
api_error500 · 503Yes, with exponential backoff.

Common error codes

invalid_api_keyThe key is wrong, revoked or from another account.
invalid_phone_numberto is not a valid number in international format.
missing_required_fieldA required field such as message or subject is missing.
insufficient_creditsYour balance can not cover the send.
sender_id_not_approvedThe from sender ID is pending or rejected.
domain_not_verifiedYour email domain has not passed SPF/DKIM checks yet.
template_not_foundtemplate_id does not exist.
whatsapp_not_linkedThe WhatsApp number is not connected. Re-link it.
recipient_unsubscribedThe email address has unsubscribed.
not_foundThe ID does not exist on this account.
conflictAlready exists, or not in a state that allows this.
rate_limitedToo many requests in a minute.

Retrying safely

  1. Retry only 429 and 5xx

    Never retry a 4xx error without changing the request. It will fail the same way.

  2. Respect Retry-After

    On a 429, wait the number of seconds in the Retry-After header.

  3. Back off exponentially

    For 5xx, wait 1s, 2s, 4s, 8s with a little random jitter, then give up and alert.

    for attempt in range(5):
        res = send()
        if res.status_code < 500 and res.status_code != 429:
            break
        time.sleep(int(res.headers.get("Retry-After", 2 ** attempt)))

Delivery failures

A 201 means talks accepted your message, not that it was delivered. Delivery can still fail later, for example when a phone is off for days. Get the final result from the delivered or failed webhook, or from GET /v1/sms/{message_id}/status, which includes error_code and error_message.

Note: Failed deliveries show up in Reports with their reason, so your support team can see what happened without reading logs.

Was this page helpful?
Powered by StdioX Labs