Errors

Every error is JSON with a stable envelope:

json
{
  "error": {
    "code": "invalid_request",
    "message": "`to` must be a valid US/Canada number in E.164 format.",
    "param": "to",
    "request_id": "req_8Fq2ZkT0aLw4nXcV7pHs"
  }
}

Request IDs

Every response from an endpoint that takes your API key, success or error, carries an X-Request-Id header (req_…), and error bodies repeat it as request_id. Look it up in the console under Logs to see the request and response as we received and sent them (API keys, secrets, verification codes, and card data are redacted; bodies are truncated at 8 KB; logs are kept for 30 days). Include it when you contact support.

Codes

StatusCodeMeaning
400invalid_requestA parameter is missing or malformed; param names it.
401invalid_api_keyMissing, malformed, revoked, or unknown key.
403live_access_requiredNeeds live access (or a live key before approval).
403test_mode_onlySandbox-only endpoint called with a live key.
403tenant_suspendedAccount suspended.
403forbiddenKey is valid but can't act on this resource (e.g. a from you don't own).
403insufficient_scopeA restricted key called outside its scopes; required_scope names the one needed. See scopes.
403spend_limit_reachedYour monthly spend limit is reached; the message was not sent or charged.
403sender_not_registeredThe from number is not linked to an approved registration yet, so it can only text your verified numbers. X-SimpleSMS-Sender-State says where it stands. See Numbers.
404not_foundNo such resource on your account.
409idempotency_conflictIdempotency-Key reused with a different payload.
429rate_limitedToo many requests; check Retry-After.
429quota_exceededA daily quota was reached.
502carrier_errorThe carrier permanently rejected the message, or did not answer after we handed it over (so we will not risk sending it twice). Temporary carrier problems do not return this: the send answers 202 with the message queued and we retry it (Messages).
500internal_errorOur fault. Retry, and tell us if it persists.

Delivery failures

A message that was accepted but not delivered has status: "failed" and a failure object, on the message and in the message.failed webhook: { code, title, explanation, action, carrier_code }. The codes are stable; switch on code, show title and explanation to your team, and follow action. carrier_code is the carrier's own code when it sent one, for support conversations.

CodeTitleWhat happened, and what to do
carrier_filteredFiltered as spamThe recipient's mobile carrier filtered this message as spam or unwanted traffic, so it never reached the phone. Recommended: identify your business in the first words, avoid link shorteners and all-caps, make sure the recipient opted in, and keep similar messages from going out in bursts. Repeated filtering on the same content usually means the wording needs to change.
unreachablePhone unreachableThe number is valid, but the phone could not be reached: it was switched off, out of coverage, or its inbox was full until the carrier gave up. Recommended: retry later (hours, not seconds). If it keeps failing, confirm the number with the recipient.
invalid_numberNumber not in serviceThe destination number does not exist or is no longer assigned to a phone. Recommended: stop sending to this number and ask the recipient for an up-to-date one. Retrying will not help.
opted_outRecipient opted outThe recipient has unsubscribed from your messages (for example by replying STOP), so the message was not sent. Recommended: do not retry. Only send again if the recipient opts back in, for example by texting START to your number.
content_blockedContent blockedThe message content is not allowed on the network, for example a prohibited topic or a blocked link. Recommended: review the Messaging Policy, change the wording or link, and send again. Resending the same text will fail the same way.
rate_limitedSending too fastToo many messages went to this number or through this route in a short time, so this one was held back. Recommended: slow down and retry with backoff. Honor the Retry-After header when the API returns 429.
landlineNumber can't receive textsThe destination is a landline or another line that does not accept text messages. Recommended: use a mobile number for this recipient. A lookup (GET /v1/lookup) tells you the line type before you send.
carrier_rejectedRejected by the carrierThe carrier refused the message before trying to deliver it, for example because the route or sender is not allowed to reach this number. Recommended: check that the sending number is set up for this kind of traffic. If every message to a carrier is rejected, contact support with the message id.
spend_limit_reachedSpending limit reachedSending this message would take your account past the monthly spending limit you set, so it was not sent and nothing was charged. Recommended: raise or remove the limit (console Settings → Spend limit, or PATCH /v1/spend-limit), or wait until the 1st (UTC). Refused messages are not queued: send them again once there is room.
sender_not_registeredSending number not registered yetThe number this was sent from is not linked to an approved registration yet, so it can only text numbers you have verified. This recipient is not one of them, so the message was not sent and nothing was charged. Recommended: open the number in the console (Numbers) to see where its registration stands and what, if anything, is needed from you. Once the number shows Active, send again. Meanwhile you can text your verified numbers.
unknownNot deliveredThe carrier reported the message as undelivered without giving a reason. Recommended: retry once later. If the same number keeps failing, contact support with the message id.

Some sends are refused before a message exists: the recipient opted out, the sending number is not registered yet, the content is blocked, the number is rate limited, or the send would pass your spending limit. Those answer 403 or 429 and the error carries the same object, so one handler covers both:

json
{
  "error": {
    "code": "recipient_opted_out",
    "message": "This recipient opted out of your messages on Oct 5, 2026 at 8:42 AM UTC by replying STOP. Nothing was sent and you were not charged. They can opt back in by texting START to your number.",
    "param": "to",
    "opted_out_at": "2026-10-05T08:42:00.000Z",
    "opted_out_via": "sms_keyword",
    "opted_out_method": "keyword",
    "failure": { "code": "opted_out", "title": "Recipient opted out", "explanation": "...", "action": "Recommended: ...", "carrier_code": null }
  }
}

Every code except content_blocked, spend_limit_reached and sender_not_registered (sandbox numbers are never registered) has a sandbox number that produces it, so you can test the handling before going live.

Success codes

200 and 201 mean done. 202 (on POST /v1/messages) means accepted and queued: a temporary carrier problem is being retried for you. A replayed idempotent request returns the original status code with an Idempotent-Replayed: true header.

Rate limits

Default 60 requests/minute per key. 429s include a Retry-After header. Successful quota-limited calls include X-Quota-Remaining.