How to Resolve "Rate limit exceeded" in API Gateway Security Policies
Quick answer
When automated clients, microservices, or frontend web applications exceed the request threshold defined in an API Gateway security policy (such as Kong, AWS...
When automated clients, microservices, or frontend web applications exceed the request threshold defined in an API Gateway security policy (such as Kong, AWS API Gateway, Nginx, or Cloudflare), the gateway rejects incoming traffic with the response code HTTP 429 Too Many Requests and the error message Rate limit exceeded. Resolving this issue involves inspecting rate-limiting response headers, optimizing client-side request batching and retry backoff, and adjusting gateway throttling policies.
The Problem
When sending requests or configuring components in your development environment, the application or HTTP client fails with the following exact error trace:
# Example 1: Standard HTTP 429 Response Header
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1672531200
{"statusCode": 429, "error": "Too Many Requests", "message": "Rate limit exceeded. Try again in 60 seconds."}
# Example 2: AWS API Gateway Throttling Exception
{
"message": "Limit Exceeded",
"code": "TooManyRequestsException"
}
# Example 3: Kong API Gateway Rate Limiting Error
{"message": "API rate limit exceeded"}
This error halts script execution, prevents API communication, or results in immediate client-side connection drops.
Why It Happens
This failure occurs due to misconfigurations at the networking, cryptographic, or application protocol layer. The primary causes include:
- Uncontrolled Polling or Loops: Frontend components or background cron jobs execute rapid poll loops without caching responses or respecting interval timers.
- Shared Client IP Throttling: API Gateway security policies limit requests by client IP address (
$remote_addr), causing multiple users behind a corporate NAT proxy or shared VPN to exhaust the collective quota instantly. - Misconfigured Burst vs. Rate Limits: Gateway rules define a strict steady-state rate (e.g., 10 req/sec) without providing an adequate burst buffer to handle brief traffic spikes during page loads.
- Distributed Microservice Spike: Multiple stateless microservice replicas execute parallel requests without a centralized rate limiter or request queue.
The Fix
Follow these step-by-step solutions to resolve the error in your environment.
Step 1: Implement Exponential Backoff and Jitter on the Client
When consuming external APIs, respect Retry-After response headers and implement exponential backoff with full jitter to avoid sending retry storms to the gateway:
async function fetchWithRetry(url, options = {}, retries = 5, backoff = 1000) {
try {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfterHeader = response.headers.get('Retry-After');
// Parse Retry-After header (in seconds) or compute exponential backoff
const delay = retryAfterHeader
? parseInt(retryAfterHeader, 10) * 1000
: backoff + Math.random() * 500;
console.warn(`Rate limit exceeded. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
return fetchWithRetry(url, options, retries - 1, backoff * 2);
}
return response;
} catch (err) {
throw err;
}
}
Step 2: Tune API Gateway Throttling and Burst Limits (Nginx Example)
If you manage the API Gateway (e.g., Nginx), adjust the rate limiting zone using limit_req_zone to include a burst parameter and nodelay processing for brief bursts:
# Define rate limit zone based on API key or IP address (10 requests/second)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
listen 443 ssl;
server_name api.example.com;
location /v1/ {
# Allow burst up to 30 requests without forcing delay
limit_req zone=api_limit burst=30 nodelay;
limit_req_status 429;
proxy_pass http://backend_cluster;
}
}
Step 3: Implement Caching Layer (Redis) to Reduce Gateway Load
Cache frequent GET endpoint responses in Redis or local memory on the application layer to avoid making redundant API requests across client calls:
import redis
import requests
import json
r = redis.Redis(host='localhost', port=6379, db=0)
def get_data(endpoint):
cache_key = f"api_cache:{endpoint}"
cached_data = r.get(cache_key)
if cached_data:
return json.loads(cached_data)
response = requests.get(f"https://api.example.com/{endpoint}")
if response.status_code == 200:
# Cache API response for 300 seconds
r.setex(cache_key, 300, json.dumps(response.json()))
return response.json()
elif response.status_code == 429:
raise Exception("Rate limit exceeded at API Gateway")
Still Not Working?
Switching Identification Keys in Gateway Policies
If your users report Rate limit exceeded despite low individual usage, your gateway is likely throttling traffic based on client IP addresses rather than authenticated API keys or JWT user IDs. Update your API Gateway security policy (e.g., AWS API Gateway Usage Plans or Kong Rate Limiting Plugin) to identify consumers using $http_authorization or $http_x_api_key instead of $remote_addr to ensure fair quota allocation per user.