"SMTP sending limits: how to design batching, pacing, and backoff"
"Turn provider quotas and temporary SMTP errors into a safe sending plan with batches, pacing, adaptive backoff, and per-workspace controls."
An outbound journey can be perfectly written and still fail operationally if 10,000 messages arrive at the sending layer as one burst.
SMTP providers, mailbox providers, domains, and IPs all apply limits. Some are published account quotas. Others are dynamic responses to volume, reputation, recipient feedback, or a sudden change in behavior. Good sending infrastructure does not try to “beat” those limits. It turns them into a controlled queue.
This guide explains the mechanics of batching, pacing, and backoff. It is not permission to send unwanted email. Use it only for lawful, relevant messaging, respect provider policies, keep suppression lists current, and make opting out straightforward.
Four limits that people incorrectly treat as one
Before choosing a batch size, separate these constraints.
1. Daily account quota
A provider may cap how many recipients an account can send to during a rolling 24-hour period. Amazon SES, for example, gives each account a regional sending quota and counts recipients rather than API calls.
2. Per-second acceptance rate
The SMTP or API endpoint may accept only a certain number of recipients per second. This protects the provider and its reputation. Short bursts may behave differently from sustained traffic, so “the first 500 worked” is not evidence that the next 50,000 should be released immediately.
3. Mailbox-provider throttling
Gmail, Microsoft, Yahoo, and other destinations can temporarily defer mail based on sender identity, IP reputation, complaint signals, authentication, traffic shape, and current load.
Google's sender guidance explicitly recommends a consistent rate, gradual volume increases, and reducing volume when messages begin bouncing or being deferred.
4. Your own reputation envelope
Your provider may technically allow more than your domain or IP should send today. New infrastructure, a new message format, or a new recipient segment needs a gradual ramp even when the account quota is high.
The safe rate is the lowest of these limits, not the largest number on the provider dashboard.
Model sending as a queue
Store one job per recipient with at least:
- workspace;
- journey and step;
- recipient;
- sending identity;
- provider connection;
- earliest send time;
- attempt count;
- status;
- idempotency key;
- suppression status.
A scheduler should release eligible jobs into small batches. A worker sends them while enforcing limits for the provider, domain, IP, mailbox destination, and workspace.
This design is more reliable than a single loop that loads every recipient and sleeps between messages. A durable queue survives restarts, supports retries, and makes “why has this contact not received step two?” answerable.
Choose a conservative starting rate
Start below the provider's maximum. Leave room for transactional mail, retries, and other workspaces sharing the same connection.
If the account can accept 20 recipients per second, a starting policy might release 5 to 10 per second in small batches. The exact value depends on sending history and provider guidance—not on a universal cold-email number.
Then watch:
- accepted messages;
- temporary deferrals;
- hard bounces;
- complaints;
- provider throttling responses;
- queue delay;
- domain and IP reputation;
- unsubscribes.
Increase only after the current level is stable. Volume should climb as evidence, not as a calendar promise.
Batch by both size and time
A batch policy needs two values:
- maximum batch size, such as 50 jobs;
- flush interval, such as every 30 seconds.
The system sends when either condition is reached. This prevents low-volume workspaces from waiting indefinitely while keeping large campaigns from releasing an unlimited burst.
For multiple clients, apply limits at several scopes:
- global service limit;
- provider credential limit;
- sending domain or IP limit;
- workspace limit;
- journey limit.
This prevents one client's large import from consuming every available sending slot.
Use a token bucket for steady pacing
A token bucket is a simple way to represent sending capacity.
Tokens enter the bucket at a fixed rate. Every recipient consumes one token. The bucket has a maximum size, allowing a small controlled burst without allowing unlimited accumulation.
Example:
- refill rate: 5 tokens per second;
- bucket capacity: 20;
- each recipient: 1 token.
After an idle period, the sender can process up to 20 recipients quickly, then settles at 5 per second. Separate buckets can enforce provider and workspace limits simultaneously.
Classify SMTP outcomes before retrying
Do not retry every failure.
Success
Record the provider message ID and advance the recipient only once. Use an idempotency key so a worker crash after acceptance does not create a duplicate message.
Temporary failure
SMTP 4xx responses generally indicate a temporary problem: throttling, greylisting, mailbox load, or a transient provider issue. Keep the job queued and retry later.
Permanent failure
SMTP 5xx responses often indicate a permanent rejection, invalid address, policy block, or message problem. Suppress or route the job for review based on the exact enhanced status code. Repeating the same rejected message can damage reputation.
Unknown outcome
A connection may drop after the provider accepted the message but before your worker recorded success. This is why provider IDs, delivery events, and idempotency matter. Do not immediately resend an unknown outcome.
Back off adaptively
For temporary failures, increase the delay after each attempt:
delay = minimum(cap, base × 2^attempt) + jitter
Jitter adds a small random interval so thousands of deferred jobs do not wake at the same second.
A practical sequence may be:
- first retry: a few minutes;
- second: tens of minutes;
- later: hours;
- stop after a defined age or attempt count.
Provider instructions override generic timing. Gmail, for example, publishes specific recovery guidance for certain rate-limit errors. Preserve the exact SMTP response so operators can apply the right policy.
Reduce rate when the system gives you a warning
Adaptive pacing should respond to the trend, not only a total failure.
Reduce the release rate when:
- temporary deferrals rise;
- provider latency increases sharply;
- reputation or spam indicators worsen;
- a new template or sending domain goes live;
- bounce rate changes;
- the provider reports quota pressure.
Pause when suppression processing, authentication, or event ingestion is unhealthy. Sending without feedback is operating blind.
Respect send windows without creating a spike
“Send at 9:00 local time” should not mean every eligible contact receives a message at 9:00:00.
Treat the time as the opening of a delivery window. Spread jobs across the permitted period while preserving journey order. If the available window is too small for the safe rate, carry remaining work forward instead of compressing it into a burst.
For global journeys, calculate eligibility in the recipient's timezone, then enqueue into the same paced sending layer.
Multi-workspace fairness
Use weighted fair scheduling so every active workspace makes progress.
A simple round-robin can release one batch per workspace. A weighted version gives larger or higher-priority workspaces more slots without starving smaller clients.
Keep contacts, templates, credentials, analytics, suppression lists, and logs isolated. Shared infrastructure should coordinate capacity, not mix tenant data.
Monitor the metrics that explain queue health
Track:
- queued jobs by workspace and journey;
- oldest eligible job;
- sends per minute;
- acceptance, temporary failure, and permanent failure rates;
- retries by attempt number;
- quota utilization;
- provider and mailbox destination;
- bounce, complaint, and unsubscribe trends;
- duplicate-prevention events.
An increasing queue is not automatically bad. It may mean pacing is protecting a provider limit. The important question is whether the queue clears inside the promised send window without quality signals deteriorating.
Infrastructure control creates responsibility
Owning the SMTP connection gives you control over provider choice, reputation, quotas, and logs. It also means you own the consequences of poor lists, sudden spikes, missing authentication, and ignored feedback.
OutboundOS keeps journeys, exact send times, branching, workspace isolation, batching, and analytics together while delivery remains on SMTP credentials you control. That lets the orchestration layer respect the actual envelope of each client's infrastructure instead of pretending every workspace has the same unlimited pipe.