"Send everything as fast as possible" is the wrong policy. ISPs throttle senders that exceed reasonable per-hour and per-recipient rates; sustained bursts trip rate-limit detectors that erode reputation before they trip the more visible block. The best-deliverability senders pace consistently below the throttle threshold of every ISP they target — not because they need the slack, but because predictable cadence is itself a positive reputation signal.
This article covers the four throttling patterns AcelleMail supports, when each is appropriate, and the field-level configuration to implement them.
Pattern 1 — Per-server throttling#
The simplest pattern: limit the rate at which a sending server emits messages. AcelleMail's SendingServer model has these fields:
| Field |
Purpose |
sending_quota |
Max messages per quota_period |
quota_period |
One of: minute, hour, day, week, month |
sending_quota_value (per second) |
Hard SMTP-rate ceiling — how fast the worker dispatches |
Daily quota caps the total volume per server per day; per-second rate caps the burst. Configure both:
Admin → Sending Servers → <server> → Edit
Sending quota: 500,000
Quota period: 1 day
Throttle (per second): 100 (= 360,000/hour ceiling, well under daily cap)
The interplay matters. With quota = 500k/day and throttle = 100/sec, the worker can drain the quota in ~83 minutes — too fast for some ISPs to digest cleanly. Spreading across the working day (target ~10/sec for a 500k/day program) gives Gmail/Microsoft adaptive throttle room to learn engagement before the next message arrives.
Pattern 2 — Per-list throttling (timing for engagement)#
A list mailed all at once has a sharp engagement spike (everyone opens within 4 hours, then nothing). A list mailed over 8 hours has a flatter spike — opens spread, click-through is roughly 15-25 % higher, and the receiver-side spam filters see your traffic over a longer window which improves classification.
In AcelleMail:
Customers → <customer> → Campaigns → New → "Send Settings"
Send rate: 10,000/hour (the campaign's max throughput)
Schedule: Best Time per Subscriber Timezone (optional, see below)
The campaign's send rate is bounded by its sending-server's quota AND the campaign's own configured rate. Setting both gives you fine-grained control: a server might be capable of 100/sec, but you cap a particular campaign at 50/sec so it doesn't dominate the queue.
Pattern 3 — Time-of-day shaping#
For B2C senders, send timing measurably affects opens. AcelleMail's "Send by recipient timezone" mode (controlled per subscriber via the timezone attribute):
Campaign → "Schedule by Subscriber Timezone"
Window: 09:00 - 18:00 recipient local
Use offset: subscriber.timezone (or list default)
The worker dispatches per-subscriber based on each recipient's local time. Implementation: AcelleMail computes the dispatch time per subscriber and queues the SES/Mailgun API call accordingly. This pattern shifts the daily peak from "your timezone 09:00" to "many smaller peaks distributed across all recipient timezones," which tends to flatten the throughput curve and reduce momentary throttling.
The cost: throughput is no longer "as fast as the worker can run" — it's gated by recipient-timezone-distribution. For a list mostly in 4 timezones, expect 4 distinct throughput peaks across 24 hours.
Pattern 4 — Adaptive backoff (auto-pause on signals)#
Per the WarmupStrategy data model, AcelleMail can auto-pause sending when bounce or complaint thresholds are crossed:
// app/Dto/WarmupStrategyData.php — relevant fields
public bool $enableSafetyChecks;
public bool $pauseOnNegativeSignals;
public float $maxBounceRate; // 0.05 = 5%
public float $maxComplaintRate; // 0.003 = 0.3%
When pauseOnNegativeSignals is on and either rate is exceeded over the rolling window, the queue worker stops dispatching for the affected sending server until an admin un-pauses it. This is the safety system protecting reputation from a content disaster (a campaign that triggers a complaint storm).
Don't disable this. The cost of a 4-hour pause that prevents a 24-hour deliverability hit is a strict win.
Configuring the worker for sustained throughput#
Two queue-worker tuning levers:
# /etc/supervisor/conf.d/acellemail-worker.conf
command=/usr/bin/php /var/www/acellemail/artisan queue:work \
--sleep=3 --tries=3 --max-time=3600 \
--memory=256
numprocs=4 # one process per ~25k/day; tune by tier
--max-time=3600 recycles the worker every hour to bound memory leaks; --memory=256 recycles when memory exceeds 256 MB (also leak protection). At Medium-tier and above, run more numprocs rather than longer-lived workers.
For SES sending, configure SES's own per-account rate limit at parity with AcelleMail's per-server rate — don't have AcelleMail dispatch faster than SES will accept; SES will start returning Throttling.Send errors that AcelleMail logs as failed jobs.
Throttle response to provider 421/4.7.0#
When the SMTP transaction returns a 421 or 4.7.0 (rate-protected defer), AcelleMail's bounce handler logs a soft bounce. The Laravel queue retries the message after 5/15/60 minutes. For a single recipient this is fine; for many simultaneous recipients (a reputation event), you want to:
- Auto-pause the sending server (Pattern 4 will do this if bounce rate exceeds threshold).
- Reduce the per-second rate by 50 % when a 421 spike is detected.
- Resume at the reduced rate; bump back up only after 12 hours clean.
AcelleMail doesn't auto-implement step 2 (yet); you can do it manually via cron + an artisan command that polls bounce rate and updates sending_quota_value per server. For most ops, the pattern-4 auto-pause is sufficient.
Diagnostic queries#
Throughput per sending-server-per-hour, last 24h:
SELECT
ss.name,
HOUR(tl.created_at) as hour,
COUNT(*) as sends
FROM tracking_logs tl
JOIN sending_servers ss ON tl.sending_server_id = ss.id
WHERE tl.created_at > NOW() - INTERVAL 24 HOUR
GROUP BY ss.id, hour
ORDER BY ss.name, hour;
Find sending-servers approaching their per-day quota:
SELECT name, sending_quota,
(SELECT COUNT(*) FROM tracking_logs
WHERE sending_server_id = ss.id
AND created_at > CURRENT_DATE) as today_sent
FROM sending_servers ss
WHERE quota_period = 'day'
ORDER BY today_sent / sending_quota DESC;
Alert when ≥ 90 % of any quota.
Related reading#
FAQ#
What's the right per-second rate?#
Depends on tier and recipient distribution. Rules of thumb:
- 50k/day program → 5/sec sustained
- 500k/day program → 25-50/sec sustained
- 5M/day program → 200-500/sec sustained, distributed across multiple sending servers
Below ~5/sec, throttling is overhead; above ~500/sec from a single IP, you're flirting with most ISPs' rate-protection.
Can I send faster on weekends when ISPs are quieter?#
Marginally. ISP rate-protection is roughly the same 24/7. The actual constraint is recipient receipt timing — sending Saturday 02:00 produces poor open rates regardless of throughput.
What if my sending server's quota is "unlimited"?#
You still want a per-second throttle. "Unlimited daily quota" with no per-second cap means a 1M-message campaign tries to send all 1M in the first minute, which trips every receiver's rate-protection.
Does throttling apply to API-based sending (SES, Mailgun)?#
Yes. The per-second rate caps how fast AcelleMail makes API calls to SES/Mailgun. Exceeds the provider's account-level rate limit → API-level rate-limit errors → soft bounces in AcelleMail.
Throttle progression chart for new sending servers#
When introducing a new sending server (warmup or capacity expansion), use this progression:
| Phase |
Daily cap |
Per-second |
Spread (campaign duration) |
Acceptance |
| Day 1-2 |
100 |
1 |
Single 8-hour window during business day |
All sends accepted, 0 deferrals |
| Day 3-7 |
1,000 |
5 |
8-hour spread |
Bounce rate < 2 %, no soft-bounce burst |
| Week 2 |
10,000 |
20 |
12-hour spread covering 2 timezones |
SNDS GREEN, Postmaster appearing |
| Week 3 |
50,000 |
50 |
12-hour spread |
Postmaster IP rep ≥ Medium |
| Week 4+ |
100,000+ |
100+ |
Per recipient timezone (24h shaped) |
Postmaster IP rep High; full rotation |
The "spread" column matters as much as the daily cap. A 50,000-message campaign sent in 2 hours from a fresh IP is dramatically worse for reputation than the same volume over 12 hours.
Reading the AcelleMail dashboard for throttle effectiveness#
The per-server hourly graph (Admin → Sending Servers → → Stats) should show roughly flat hourly throughput during the campaign window. Spike patterns indicate problems:
- Single tall spike at hour 0 — your throttle isn't working; messages are bursting from the worker.
- Sawtooth pattern — workers are recycling rapidly, possibly due to memory limits. Bump
--memory=512.
- Plateau then sudden drop — you hit the daily quota mid-campaign; bump the quota or split across more servers.
- Plateau with a dip mid-window — receiver-side throttling (often Microsoft RP-002). Investigate SNDS for the affected IP.
The healthy pattern is a flat rectangle. Hour-to-hour variance < 20 % means the throttle is doing its job.