← Back to docs

Errors & Rate Limits

The error format every EuroMail API call uses, what each error code means, and how to handle 429 responses and retries.

Error format

Every failed request returns a JSON body with a single error object:

{
  "error": {
    "type": "validation_error",
    "code": "VALIDATION_ERROR",
    "message": "Recipient '[email protected]' is on the suppression list"
  }
}
  • code is the field to branch on in your code. It is stable and upper-case.
  • type is a broader group (auth_error, validation_error, rate_limited and so on). Several codes can share one type.
  • message is meant for people. It names the field, address or limit involved, and its wording can change, so don't parse it.
  • Some errors also carry docs_url, a link to the relevant part of this page. Most errors don't have one yet, so treat it as optional.

A few errors add fields of their own, listed below.

Error codes

HTTP statuscodetypeWhat it meansRetry?
401AUTH_FAILEDauth_errorThe API key is missing, malformed, revoked or unknown.No. Fix the key.
403FORBIDDENforbiddenThe key is valid but may not do this, for example sending from a domain that isn't on the account.No
403INSUFFICIENT_SCOPEpermission_errorThe key lacks the scope this endpoint needs. The message names the scope.No. Use a key with that scope.
403ACCOUNT_SUSPENDEDaccount_suspendedSending is paused on the account. The body adds suspended_at.No. Contact support.
403CSRF_FAILEDauth_errorA browser session request that changes data was sent without X-Requested-With: XMLHttpRequest. API-key requests never get this.No. Add the header.
404NOT_FOUNDnot_foundThe resource doesn't exist or belongs to another account.No
409CONFLICTconflictThe resource already exists, such as a second signup form for the same list.No
422VALIDATION_ERRORvalidation_errorThe request body is invalid, or the send breaks a rule: a suppressed recipient, an unverified domain sending to someone other than you, a field over its limit.No. Fix the request.
422ATTACHMENT_REFUSEDattachment_refusedAn attachment is a blocked file type or failed the virus scan. Nothing was sent.No
429RATE_LIMIT_EXCEEDEDrate_limitedToo many requests or sends. See Rate limits.Yes, after a pause
429QUOTA_EXCEEDEDquota_exceededThe monthly sending allowance is used up.Yes, once retry-after has passed or the allowance is raised
500INTERNAL_ERRORinternal_errorSomething failed on our side.Yes, with backoff
503SERVICE_UNAVAILABLEservice_unavailableA dependency is briefly unavailable.Yes, with backoff
503ATTACHMENT_SCAN_UNAVAILABLEservice_unavailableThe virus scanner couldn't answer, so nothing was sent. The response has retry-after.Yes, the whole request

A small number of internal failures report DATABASE_ERROR or REDIS_ERROR with status 500. Treat them like INTERNAL_ERROR.

Authentication

A 401 means the request carried no usable key. Send it as Authorization: Bearer em_live_... (or in the X-EuroMail-Api-Key header) and check that the key still exists in the dashboard. A 403 means the key works but lacks permission: check its scopes on the API Keys page. See API Keys & Authentication.

Request format

A 422 comes with a message that says what to change. The most common causes when sending:

  • the from domain is added but not verified, and the recipient isn't your own account email (see Sandbox mode);
  • the recipient is on your suppression list;
  • a required field is missing, or a value is over its limit.

The Sending Emails guide lists every field.

Not found

A 404 is also what you get for another account's resource, so a correct ID used with the wrong account's key looks the same as a wrong ID.

Rate limits

Several limits apply at once. Each one answers with 429:

LimitValueApplies to
Per account50 requests per secondEvery authenticated API request
Whole API500 requests per secondAll traffic, before authentication
Send rate, first 24 hours5 emails a minute, 25 an hourSends to anyone but yourself
Send rate, days 2 to 715 emails a minute, 75 an hourSends to anyone but yourself
Send rate, from day 8100 emails a minute, 500 an hourSends to anyone but yourself
New-account allowance20 recipients, refilling at 2 an hour, for the first 24 hours (1,000 once an invoice is paid)Counted per recipient, including cc and bcc
Monthly allowanceYour plan's volume; 3,000 until a non-zero invoice has been paidCounted per recipient

Account age is counted from when the account was created. Sandbox sends (from an unverified domain to your own address) skip the send-rate limits and the new-account allowance. Each email in a batch counts on its own against the send rate.

Rate-limit headers

HeaderWhen it is setMeaning
x-ratelimit-limitEvery authenticated response, and on a 429 from the request limitsThe requests-per-second limit that applies (50 per account)
x-ratelimit-remainingOnly on a 429 from the request limitsAlways 0. It is not a live countdown.
retry-afterOn a 429 from the request limits, the new-account allowance and the monthly allowance, and on ATTACHMENT_SCAN_UNAVAILABLESeconds to wait before trying again
x-quota-limit, x-quota-usedOn QUOTA_EXCEEDEDThe monthly allowance and how much of it is used

A 429 from the per-minute or per-hour send rate has no retry-after. The message says which window was exceeded; wait for the next minute or hour.

Retrying safely

  • Retry 429, 500 and 503. Don't retry other 4xx errors: the same request will fail the same way.
  • Wait for retry-after when it is present. Otherwise back off exponentially, for example 1, 2, 4 and 8 seconds, with some random jitter.
  • Put an idempotency_key in the JSON body of every send you might retry. It makes a retry return the original email instead of sending a second one. The key must be in the body: an Idempotency-Key HTTP header is ignored. See Idempotency.
  • The TypeScript SDK retries network errors, 429 and 5xx responses for you (maxRetries, default 2), and the PHP SDK does when you set max_retries. With the other SDKs, add the retry yourself. See SDKs.

Send your first email in about 90 seconds

The free tier includes 3,000 emails a month. Email storage and AI processing in Finland, with a DPA on every plan.

Create free account See live delivery data