Per-ISP rate limits enforced
Why it matters
Mailbox providers impose strict per-IP connection and message limits that differ significantly: Gmail accepts roughly 250 SMTP connections per hour from a new IP, Yahoo and AOL limit to around 20 connections per hour per IP, and Outlook uses session-based hourly quotas. RFC5321-s4.5.3 makes clear that exceeding these limits results in 421 temporary deferrals. When all recipients are throttled identically — ignoring which ISP is receiving — the system will inevitably over-deliver to restrictive providers, accumulating 421 responses that reduce effective throughput and signal poor sending behavior, eroding IP reputation at the exact providers where limits are tightest.
Severity rationale
High because ISP-agnostic throttling causes 421 deferrals and throughput reduction at Yahoo and Outlook specifically, where per-IP limits are tightest, with repeated deferrals contributing to IP reputation degradation.
Remediation
Map receiving domain to ISP and enforce per-ISP limits before dispatching. This configuration covers the three mailbox providers responsible for the majority of consumer email:
const ISP_LIMITS: Record<string, { maxPerHour: number; maxConcurrent: number }> = {
'gmail.com': { maxPerHour: 250, maxConcurrent: 10 },
'yahoo.com': { maxPerHour: 100, maxConcurrent: 5 },
'aol.com': { maxPerHour: 100, maxConcurrent: 5 },
'outlook.com': { maxPerHour: 150, maxConcurrent: 8 },
'hotmail.com': { maxPerHour: 150, maxConcurrent: 8 },
'live.com': { maxPerHour: 150, maxConcurrent: 8 },
'default': { maxPerHour: 200, maxConcurrent: 10 },
}
export function getIspLimit(recipientDomain: string) {
return ISP_LIMITS[recipientDomain.toLowerCase()] ?? ISP_LIMITS['default']
}
Bucket your queue workers by recipient ISP and apply a Bottleneck or similar rate limiter per bucket. Cross-reference deliverability-engineering.sending-throttling.adaptive-throttling — per-ISP limits are the static floor; adaptive throttling handles dynamic 4xx signals on top.
Detection
-
ID:
per-isp-limits -
Severity:
high -
What to look for: Enumerate all ISP-specific rate limit configurations. Search for code that identifies the receiving ISP of a recipient and applies ISP-specific connection or message rate limits. Key limits vary widely: Gmail accepts ~250 connections/hour from a new IP, Yahoo/AOL limits to ~20 connections/hour per IP, Outlook.com uses hourly SMTP session limits. Look for a configuration object that maps ISP domains or IP ranges to their respective limits, and for code that selects the correct limit before dispatching.
-
Pass criteria: A per-ISP limit configuration exists covering at least 3 major ISPs (Gmail, Yahoo/AOL, Microsoft) with distinct limits. Report the count of ISPs configured even on pass.
-
Fail criteria: All recipients are throttled identically regardless of their mailbox provider, or no throttling is applied at the per-ISP level.
-
Skip (N/A) when: The project sends fewer than 1,000 emails per day where per-ISP limits are unlikely to be hit, or uses an ESP's managed sending infrastructure that handles ISP throttling transparently.
-
Cross-reference: Check
deliverability-engineering.sending-throttling.adaptive-throttling— per-ISP limits are static; adaptive throttling responds dynamically to 4xx deferrals. -
Detail on fail:
"No per-ISP throttling found — all recipients subject to the same rate, risking 421 deferrals from Yahoo and Microsoft when their per-IP limits are exceeded"or"Throttling configured globally but not differentiated by receiving mailbox provider" -
Remediation: Map receiving domains to ISP policies and throttle accordingly:
const ISP_LIMITS: Record<string, { maxPerHour: number; maxConcurrent: number }> = { 'gmail.com': { maxPerHour: 250, maxConcurrent: 10 }, 'googlemail.com': { maxPerHour: 250, maxConcurrent: 10 }, 'yahoo.com': { maxPerHour: 100, maxConcurrent: 5 }, 'ymail.com': { maxPerHour: 100, maxConcurrent: 5 }, 'aol.com': { maxPerHour: 100, maxConcurrent: 5 }, 'outlook.com': { maxPerHour: 150, maxConcurrent: 8 }, 'hotmail.com': { maxPerHour: 150, maxConcurrent: 8 }, 'live.com': { maxPerHour: 150, maxConcurrent: 8 }, 'default': { maxPerHour: 200, maxConcurrent: 10 }, } export function getIspLimit(recipientDomain: string) { return ISP_LIMITS[recipientDomain.toLowerCase()] ?? ISP_LIMITS['default'] } // In your queue worker, bucket sends by ISP and apply the relevant rate limiter
External references
- external · RFC5321-s4.5.3 — SMTP — Sizes and Timeouts (connection-level rate guidance)
- iso-25010:2011 · performance-efficiency — ISO 25010 Performance Efficiency characteristic
Taxons
History
- 2026-04-18·v1.0.0·Initial import from deliverability-engineering·automated