SMTP
Rate limits are per provider, and ignoring that costs you
A receiving mail server will accept only so much from one sender in a given period. Exceed it and you get a 4xx deferral; keep pushing and the deferral becomes a block.
The limits are not published in a form you can rely on, they differ per provider, and they vary with your reputation — a trusted sender gets more headroom than an unknown one. So the only durable strategy is to react to what the receiver tells you rather than to guess.
The dimensions
Concurrent connections. How many simultaneous sessions one provider will accept from you. Opening more produces refusals that cost nothing but noise.
Messages per connection. Many providers close a session after a number of messages. Reconnecting is normal; what matters is not fighting it.
Messages per unit time. The real limit, and the one that varies most with reputation.
Recipients per message. Relevant only if you put many recipients on one envelope, which for transactional mail you should not.
Reading the response
Providers usually say what is wrong, in text, in the SMTP response. Rate-limit responses mention rate limiting, often with a URL. This is the most under-read diagnostic in email.
Distinguish three cases, because they want different responses:
- Rate limited — slow down, retry later. Nothing is wrong with the message.
- Temporarily deferred for reputation — slowing down helps, but the underlying issue is reputation, not speed.
- Blocked — retrying does not help and makes it worse. Stop, fix, and request delisting.
Backing off properly
Exponential backoff with jitter, per destination domain. Per domain matters: being throttled by one provider is no reason to slow delivery to the others, and a global backoff turns one provider's busy hour into a general outage.
Cap the retry window. Mail that cannot be delivered in a few days is not going to be, and transactional mail has a shorter useful life than that — a password reset delivered on Thursday for a Tuesday request is worse than a bounce.
Shaping the queue
Group the queue by destination domain and apply a concurrency limit to each group. This is what a purpose-built MTA does by default and what a naive worker pool does not — a pool that takes the next message regardless of destination will open twenty connections to the same provider the moment a batch skews that way.
Start conservative. Two to four concurrent connections per major provider, raised only when the response data says there is room.
Verify your configuration does something
Worth checking that the throttling you configured is actually in effect. Postfix, for instance, silently ignores destination rate parameters that are not attached to a transport the mail actually uses, and logs them as unused — a configuration that looks correct and does nothing. Check the logs for unused-parameter warnings before you trust it.
DKIM signing, SPF alignment and DMARC are set up the moment you verify a domain.