# Global system rate limit as safety net

- **Pattern:** `ab-000939` (`deliverability-engineering.sending-throttling.global-rate-limit`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000939
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000939)

## Why it matters

Per-campaign and per-ISP limits are only as safe as their configuration — a misconfigured campaign with no `maxPerHour` and a per-ISP limit set too high can still dispatch thousands of messages per minute if there is no outer bound. A global system rate limit is the circuit breaker: regardless of what any campaign or ISP limiter is configured to allow, the system as a whole cannot exceed this ceiling. This single control prevents a configuration error, a bug in rate limit logic, or a runaway queue worker from causing catastrophic volume spikes that would require weeks of reputation recovery.

## Severity rationale

Low because the global limit is a safety net that only matters when other rate controls are misconfigured or absent, but its absence means there is no backstop against cascading misconfiguration at other levels.

## Remediation

Implement the global rate limiter as the outermost layer all sends pass through, configurable via environment variable so it can be adjusted without deployment:

```ts
const GLOBAL_MAX_PER_MINUTE = parseInt(
  process.env.EMAIL_GLOBAL_MAX_PER_MINUTE ?? '500'
)

const systemRateLimiter = new Bottleneck({
  reservoir: GLOBAL_MAX_PER_MINUTE,
  reservoirRefreshAmount: GLOBAL_MAX_PER_MINUTE,
  reservoirRefreshInterval: 60_000,
})

// Every send in the system goes through this first
export async function scheduleEmail(job: EmailJob): Promise<void> {
  await systemRateLimiter.schedule(() => sendWithThrottle(job))
}
```

Set `EMAIL_GLOBAL_MAX_PER_MINUTE` in your environment configuration (not hardcoded) and document the rationale for the chosen value. Add a monitoring hook that logs when the reservoir drops below 20% of capacity, signaling the system is approaching the ceiling.

## Detection

- **ID:** `global-rate-limit`
- **Severity:** `low`
- **What to look for:** Count all system-level rate limit definitions. Look for a global maximum send rate that acts as a circuit breaker for the entire system, independent of per-campaign or per-ISP limits. This is the "never exceed X emails per minute across all campaigns combined" guardrail. Check for constants like `GLOBAL_MAX_SENDS_PER_MINUTE`, `SYSTEM_RATE_LIMIT`, or equivalent. Also check if this limit is configurable via environment variable without code deployment.
- **Pass criteria:** At least 1 global system-level rate limit exists and is enforced as the outer bound. The limit is configurable via environment variable. The system logs or alerts when the global limit is being approached.
- **Fail criteria:** No global system rate limit exists — per-campaign and per-ISP limits are the only controls, leaving no safety net if those limits are misconfigured.
- **Skip (N/A) when:** Sending is routed entirely through an ESP that enforces global account-level limits and the codebase has no control over raw throughput.
- **Detail on fail:** `"No global system rate limit configured — a misconfigured campaign could exceed safe sending thresholds without any system-level circuit breaker"` or `"Per-ISP limits exist but no aggregate system cap"`
- **Remediation:** Implement a global rate limiter as the outermost control:

  ```ts
  // Environment-configurable global ceiling
  const GLOBAL_MAX_PER_MINUTE = parseInt(process.env.EMAIL_GLOBAL_MAX_PER_MINUTE ?? '500')

  const systemRateLimiter = new Bottleneck({
    reservoir: GLOBAL_MAX_PER_MINUTE,
    reservoirRefreshAmount: GLOBAL_MAX_PER_MINUTE,
    reservoirRefreshInterval: 60_000,  // 1 minute
  })

  // All sends go through this first, then per-ISP limiters
  export async function scheduleEmail(job: EmailJob): Promise<void> {
    await systemRateLimiter.schedule(() => sendWithThrottle(job))
  }
  ```

---

## External references

- iso-25010 reliability.availability

Taxons: cost-efficiency

HTML version: https://auditbuffet.com/patterns/ab-000939
