New guide: how to warm up a dedicated IP without hurting your reputation. Read now →
Log in
MigoSMTP
SMTP and APIUpdated 15 Oct 2026

Rate Limits, Retries and Backoff: Sending Reliably When Things Go Wrong

Every sending system eventually meets a limit or a temporary failure. This guide explains how rate limits work, how to tell temporary errors from permanent ones, how to retry with exponential backoff and jitter, and how to avoid sending the same message twice.

TK
Written by Tomasz Kowalski
Infrastructure and Reliability Engineer
Reviewed by Karan Malhotra
  • Published28 Jul 2026
  • Read time8 min
  • Length1,580 words
Share this guide

Email rate limits and retries are part of every serious sending system. Networks fail, servers get busy and platforms protect themselves from overload. An application that treats every error the same will either lose messages or hammer a struggling service. This guide explains the types of limits you will meet, how to read the signals you get back, and how to retry in a way that is polite, safe and effective.

429status for too many requests
4xx / 5xxSMTP classes for temporary and permanent failure
1unique key per logical message prevents duplicates

Why limits exist

Sending platforms limit traffic for several reasons. They protect shared infrastructure from a single customer using too much of it, they slow down abuse such as a stolen key used to send spam, and they follow the pace that receiving providers accept. Receiving servers apply their own limits as well. A mailbox provider may accept only a certain number of connections or messages from one address at a time, and may answer further attempts with a temporary deferral. Neither kind of limit is a fault. It is a signal to adjust your pace.

Kinds of limit you may meet

LimitWhat it controlsTypical signal
Request rateHow many API calls per second or minuteHTTP 429, sometimes with Retry-After
ConcurrencyHow many connections or requests at the same timeConnection refused, 429 or SMTP 421
Daily or monthly quotaTotal messages in a longer periodA specific error body naming the quota
Per-recipient paceMail to one receiving domain, set by that domainSMTP 4xx deferral from the receiver
Account stateLimits on a new or under-review accountAn error stating the account limit

The exact numbers depend on your plan and account. Check your dashboard and the API reference for the values that apply to you rather than assuming them.

Step 1: Classify the failure

The most useful habit is to sort every failure into one of three classes before deciding what to do.

  • Temporary. The same request might succeed later. Examples are timeouts, network errors, HTTP 429, HTTP 500, 502, 503 and 504, and SMTP replies that start with 4.
  • Permanent. The same request will keep failing. Examples are an invalid recipient, a sender that is not verified, and SMTP replies that start with 5.
  • Your request is wrong. The request is malformed or unauthorised. Examples are HTTP 400, 401, 403 and 422. Retrying changes nothing until you fix it.
SignalClassAction
Timeout, connection resetTemporaryRetry with backoff; use an idempotency key
HTTP 429TemporaryWait, honour Retry-After, retry
HTTP 500, 502, 503, 504TemporaryRetry with backoff
SMTP 421, 450, 451, 452TemporaryRetry later; the receiver asked you to wait
HTTP 400, 422Request errorFix the data; do not retry as is
HTTP 401, 403, SMTP 535Credential or permission errorCheck the key or login; alert a human
SMTP 550, 553, 554PermanentDo not retry; record and suppress the address if it is invalid

For the meaning of specific SMTP codes, see our guide to SMTP bounce codes.

Step 2: Honour the signals you receive

When a service answers 429, it is telling you to slow down. Many services add a Retry-After header, which gives either a number of seconds or a date. Use it. It is better information than any guess you can make. If there is no header, fall back to your backoff schedule.

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

{"error":"rate_limited","message":"Too many requests"}

The example is illustrative; check your platform reference for the exact fields it returns.

With SMTP, a 421 or 4xx reply to a command means the same thing in a different form. The server asks you to try later. Close the connection politely, wait and open a new one. Do not open many parallel connections to work around the limit, because that is likely to make matters worse.

Step 3: Retry with exponential backoff and jitter

Backoff means waiting longer after each failure. Exponential backoff doubles the wait each time. Jitter adds a random amount so that many clients do not retry in step. A simple and widely used form is to pick a random wait between zero and the current ceiling, then double the ceiling up to a maximum.

AttemptCeilingExample wait with jitter
11 second0.4 seconds
22 seconds1.3 seconds
34 seconds2.1 seconds
48 seconds5.7 seconds
516 seconds11.0 seconds
6 and later60 seconds (cap)Up to 60 seconds
delay = 1
for attempt in range(max_attempts):
    response = send(message)
    if response.ok or response.is_permanent:
        break
    wait = response.retry_after or random.uniform(0, delay)
    sleep(wait)
    delay = min(delay * 2, 60)

The sketch uses plain pseudocode. Adapt it to your language and add logging at each failure. The important parts are the cap on the delay, the cap on attempts and the use of the server value when one is supplied.

Set limits on both attempts and age

Every message type needs a deadline. A one-time sign-in code that arrives after fifteen minutes is worse than none, because it may confuse the user. A receipt can be late by an hour without harm. Decide a maximum age per message type, stop retrying when it passes, and tell the user or log a failure. RFC 5321 describes mail servers retrying delivery for days, which suits server-to-server relay. Your application will usually want something much shorter.

Step 4: Avoid duplicates

Retries create a risk. If a request times out, you do not know whether the platform processed it. If you send again, the recipient might get two copies. There are two ways to deal with this.

  1. Idempotency keys. This is a widely used pattern in APIs in which you attach a unique value to a request, and the server treats a repeat of the same value as the same operation. If your platform supports it, generate one key per logical message and reuse it on every retry. Check the API reference to see whether and how it is supported.
  2. Your own record. Store a message record with a status before you send. Mark it accepted when the platform returns an identifier. Before any retry, check whether an identifier was already stored.

Do not rely on retries alone for money or security mail. For messages where a duplicate is harmful, such as one-time codes, keep your own record of the latest valid code and invalidate older ones, so that a duplicate email does no harm.

Step 5: Queue instead of sending inline

Sending mail in the middle of a user request makes the user wait for your slowest dependency. A better design is to write a job to a queue and let a worker send it. The worker can control the pace, retry in the background and spread bursts over time. When a limit is reached, the queue grows for a while instead of users seeing errors.

  • Limit the number of workers so that you stay within your concurrency allowance.
  • Use a token bucket or a fixed pace to keep requests below your rate limit.
  • Send batches where the API accepts several messages per request.
  • Give urgent mail, such as sign-in codes, its own queue so that a newsletter cannot delay it.

A worked example: a spike after a product launch

A shop announces a sale. Within a minute, thousands of customers check out and the application tries to send thousands of order confirmations. The platform starts answering some requests with 429. The old code retried immediately in a loop, which kept the application at the limit and slowed every checkout. The new design does three things. Confirmations are written to a queue. Four workers send at a steady pace below the documented request limit. On a 429, a worker reads Retry-After, waits, and retries with jitter, and each message carries a unique key so that no customer receives two confirmations.

During the spike the queue grows and some confirmations arrive a few minutes late. After the spike the queue drains by itself. Customers see a normal checkout, support sees few tickets, and the operations team gets an alert only if the queue age passes a threshold they chose. The same traffic that once caused errors now causes a short delay, which is the goal of a good retry design.

Step 6: Watch and alert

Retries hide problems if you do not look at them. Track a few numbers: how many messages needed at least one retry, how many were abandoned after the last attempt, the age of the oldest queued message, and the share of requests that returned 429. A rise in any of these is an early warning. Our guide to delivery event webhooks shows how to receive the later outcome of each message, which is the other half of the picture: a request can be accepted and the message still bounce later.

Common mistakes

  • Retrying permanent errors. It wastes time and, with invalid addresses, can harm your reputation. Record the failure and suppress the address, as described in suppression list management.
  • Tight retry loops. Retrying with no delay turns a short problem into a long one.
  • No cap. Unlimited retries can fill a queue and keep stale mail alive for days.
  • Ignoring Retry-After. The server knows better than your guess.
  • Sharing one key across systems. One busy system then uses the limit for all. Use separate credentials, as in our API key guide.
  • Retrying after bad credentials. A 401 or 535 will not fix itself. Alert a person.

If you have not yet decided between SMTP and an API, read our comparison of SMTP and API sending. The rules in this guide apply to both.

Need higher limits or help with a retry pattern? Describe your expected volume and we will advise on a safe setup.

Contact support

Further reading and sources

Frequently asked questions

What does HTTP 429 mean when sending email?

It means too many requests were made in a given time. Slow down, wait for the period in the Retry-After header if one is provided, and then try again. Do not retry immediately in a tight loop.

Should I retry every failed email?

No. Retry temporary failures such as timeouts, 429 responses, server errors and SMTP 4xx replies. Do not retry permanent failures such as an invalid address or a rejected sender, because the result will not change.

What is exponential backoff?

A retry pattern in which the wait grows after every attempt, for example 1, 2, 4, 8 and 16 seconds. It gives a struggling system time to recover and stops your code from adding load when it is already under pressure.

Why add jitter to retries?

Without randomness, many clients that failed at the same moment retry at the same moment, which recreates the spike. A small random variation spreads the retries out.

How do I stop a retry from sending a duplicate?

Give each logical message a unique identifier and, where your platform supports an idempotency key, send it with the request. If your platform does not, keep your own record of what has been accepted and check it before resending.

How long should I keep retrying?

It depends on the message. A password reset code is useless after a few minutes, while an invoice can wait hours. Set a maximum age for each message type and stop when it passes. RFC 5321 suggests that servers keep trying delivery for several days, but your application usually needs a shorter limit.

What is the difference between a rate limit and a quota?

A rate limit controls how fast you can send, for example requests per second. A quota controls how much you can send over a longer period, such as per day. You can hit either, and the responses may differ.

Does a timeout mean the message was not sent?

Not necessarily. The request may have been processed even though the response never arrived. This is why idempotency keys or your own record of sent messages matter before you retry after a timeout.

What if I keep hitting the limit?

Spread sending over time with a queue, reduce unnecessary messages, and send in batches where the API allows it. If your legitimate volume needs more headroom, contact support with the pattern you expect.

Topics#rate limits#retries#backoff#429#idempotency#smtp#api#reliability
Was this guide helpful?Your answer helps us improve our documentation.

Start sending in minutes.

Create an account, add your domain and send your first email.

Start free Talk to sales