Basics

Errors & rate limits

Errors use conventional HTTP status codes and a small, stable JSON body. Branch on the code field, not on the message.

Error format

Every non-2xx response has a JSON body with a machine-readable code and a human-readable message:

Error response
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "code": "invalid_parameter",
  "message": "sender.email is missing or invalid"
}

Messages may change over time and are meant for logs and humans. Codes are part of the API contract.

Status codes

StatusCodeMeaning
200 / 201 / 204—Success. Sends and creates return 201 Created; contact updates/deletes return 204 No Content.
400invalid_parameterThe body is not valid JSON, a required field is missing, an address is invalid, the sender domain is not verified, the template is inactive or has a syntax error.
400duplicate_parameterPOST /v3/contacts was called for an existing email without updateEnabled: true.
401unauthorizedMissing, unknown or revoked API key.
402quota_exceededThe brand's monthly email quota is used up. Upgrade the plan or buy an add-on pack.
403permission_deniedThe API key lacks the permission this endpoint requires.
403email_not_verifiedThe brand owner has not verified their email address yet.
403account_suspendedSending is disabled for this brand.
404document_not_foundUnknown templateId or message id.
409whatsapp_not_connectedWhatsApp send attempted before a number was connected.
422outside_service_windowWhatsApp free-form text sent outside the 24-hour customer service window.
429too_many_requestsRate limit exceeded. Wait for Retry-After seconds.
5xxinternal_errorSomething went wrong on our side. Safe to retry with backoff.

Rate limits

Each brand has a token-bucket rate limit shared by all of its API keys and the SMTP relay. Every API request takes one token; the bucket refills continuously at your plan's rate.

PlanRequests per secondMonthly emails
Starter15/s40,000
Business50/s150,000
Enterprise150/s600,000

When the bucket is empty the API answers with 429 and a Retry-After header (in seconds):

429 response
HTTP/1.1 429 Too Many Requests
Retry-After: 1
Content-Type: application/json

{"code": "too_many_requests", "message": "Rate limit exceeded"}

Over SMTP, the same limit is reported as the temporary error 451 4.7.1; standard mail clients retry automatically.

Retrying

  • Retry 429 after the number of seconds in Retry-After, and 5xx or network errors with exponential backoff plus jitter.
  • Do not retry other 4xx errors unchanged — fix the request (or the account state) first.
  • To send many messages at once, prefer messageVersions (up to 1000 per request) over one request per recipient; it uses a single rate-limit token.
# curl retries 429 and 5xx responses on its own and honours Retry-After
curl -X POST 'https://app.snappymails.com/v3/smtp/email' \
  --retry 5 --retry-max-time 60 \
  -H "api-key: $SNAPPYMAILS_API_KEY" \
  -H 'Content-Type: application/json' \
  -d @message.json

Quota vs. rate limit

The rate limit (429) is about speed and recovers within seconds. The quota (402) is about volume and only resets with the next billing month or a plan change, so retrying will not help.