Skip to content

Rate Limiting

This guide covers configuring rate limiting to protect License Monitor API from abuse and ensure fair usage.

Rate limiting prevents:

  • Denial of Service (DoS) attacks
  • Brute force authentication attempts
  • API abuse by misbehaving clients
  • Resource exhaustion from runaway scripts

Configure rate limits in config.toml:

[api]
enabled = true
bind_address = "127.0.0.1"
bind_port = 8080
# Rate limiting configuration
rate_limit_enabled = true
rate_limit_requests = 100 # Requests allowed
rate_limit_window_seconds = 60 # Per time window
rate_limit_burst = 20 # Allow burst above limit
OptionDescriptionDefault
rate_limit_enabledEnable rate limitingtrue
rate_limit_requestsMax requests per window60
rate_limit_window_secondsTime window in seconds60
rate_limit_burstAdditional burst capacity10
rate_limit_byLimit key (ip, api_key, user)ip

Default strategy - limit requests per client IP:

[api.rate_limit]
enabled = true
strategy = "ip"
requests = 100
window_seconds = 60

Limit requests per API key (useful for multi-tenant):

[api.rate_limit]
enabled = true
strategy = "api_key"
requests = 1000
window_seconds = 60

Different limits for different endpoints:

[api.rate_limit]
enabled = true
default_requests = 100
default_window = 60
# Stricter limits for expensive operations
[api.rate_limit.endpoints."/api/execute"]
requests = 10
window_seconds = 60
# More lenient for health checks
[api.rate_limit.endpoints."/api/health"]
requests = 300
window_seconds = 60
# Stricter for authentication
[api.rate_limit.endpoints."/api/auth"]
requests = 5
window_seconds = 300

License Monitor returns rate limit information in response headers:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed
X-RateLimit-RemainingRequests remaining in window
X-RateLimit-ResetUnix timestamp when window resets
Retry-AfterSeconds to wait (on 429 response)
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1704067200
Content-Type: application/json
{"status": "ok"}
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1704067200
Retry-After: 45
Content-Type: application/json
{
"error": "Rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please retry after 45 seconds.",
"retryAfter": 45
}
async function fetchWithRateLimit(url: string, options: RequestInit = {}) {
const response = await fetch(url, options);
// Check rate limit headers
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining') || '0');
const reset = parseInt(response.headers.get('X-RateLimit-Reset') || '0');
if (remaining < 10) {
console.warn(`Rate limit warning: ${remaining} requests remaining`);
}
// Handle 429 response
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
console.warn(`Rate limited. Retrying after ${retryAfter} seconds`);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return fetchWithRateLimit(url, options);
}
return response;
}
async function fetchWithBackoff(
url: string,
options: RequestInit = {},
maxRetries = 3
) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
const retryAfter = parseInt(response.headers.get('Retry-After') || '0');
const backoff = retryAfter || Math.pow(2, attempt) * 1000;
console.log(`Rate limited. Waiting ${backoff}ms before retry ${attempt + 1}`);
await new Promise(resolve => setTimeout(resolve, backoff));
}
throw new Error('Max retries exceeded');
}
# Define rate limit zones
limit_req_zone $binary_remote_addr zone=api_general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api_auth:10m rate=1r/s;
limit_req_zone $http_x_api_key zone=api_by_key:10m rate=100r/s;
server {
# General API endpoints
location /api/ {
limit_req zone=api_general burst=20 nodelay;
limit_req_status 429;
# Custom error page for rate limiting
error_page 429 = @rate_limited;
proxy_pass http://license_monitor;
}
# Stricter rate limiting for auth
location /api/auth {
limit_req zone=api_auth burst=5 nodelay;
limit_req_status 429;
proxy_pass http://license_monitor;
}
# Rate limit by API key for specific clients
location /api/v2/ {
limit_req zone=api_by_key burst=50 nodelay;
limit_req_status 429;
proxy_pass http://license_monitor;
}
location @rate_limited {
default_type application/json;
return 429 '{"error":"Rate limit exceeded","code":"RATE_LIMIT_EXCEEDED"}';
}
}
frontend http_front
bind *:80
# Define rate limit table
stick-table type ip size 100k expire 1m store http_req_rate(1m)
# Track requests per IP
http-request track-sc0 src
# Deny if rate exceeds 100 req/min
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }
default_backend license_monitor
backend license_monitor
server monitor1 127.0.0.1:8080
Terminal window
# View rate limit metrics
curl -H "X-API-Key: $API_KEY" \
http://localhost:8080/api/metrics | jq '.rateLimits'
# Output:
# {
# "totalRequests": 15234,
# "limitedRequests": 45,
# "currentWindowRequests": 23,
# "topLimitedClients": [
# {"ip": "192.168.1.100", "limitedCount": 20},
# {"ip": "192.168.1.101", "limitedCount": 15}
# ]
# }
Terminal window
# Find rate-limited requests in logs
grep "RATE_LIMIT_EXCEEDED" /var/log/license-monitor/license_monitor.log
# Count rate-limited requests by IP
grep "RATE_LIMIT_EXCEEDED" /var/log/license-monitor/license_monitor.log | \
awk '{print $NF}' | sort | uniq -c | sort -rn
# Prometheus alerting rule
groups:
- name: rate_limits
rules:
- alert: HighRateLimitHits
expr: rate(license_monitor_rate_limit_exceeded_total[5m]) > 10
for: 5m
labels:
severity: warning
annotations:
summary: High rate of rate-limited requests
description: More than 10 requests/sec are being rate limited
Use CaseRequests/minBurst
Dashboard refresh6010
Monitoring integration12020
CI/CD automation305
API development30050
[api.rate_limit]
enabled = true
requests = 100
window_seconds = 60
# Whitelist monitoring systems
whitelist_ips = [
"10.0.1.50", # Prometheus
"10.0.1.51" # Grafana
]
# Higher limits for specific API keys
[api.rate_limit.api_key_overrides]
"monitoring-key-xyz" = { requests = 600, window_seconds = 60 }
"ci-cd-key-abc" = { requests = 300, window_seconds = 60 }
[api.rate_limit]
enabled = true
requests = 100
window_seconds = 60
# Instead of 429, queue requests when near limit
queue_when_near_limit = true
queue_threshold = 90 # Queue when at 90% of limit
queue_max_wait_seconds = 30