Sendry provides multi-level rate limiting to protect against abuse and ensure fair resource usage.
Rate limiting operates at multiple levels, each with independent counters:
| Level | Description | Example Key | Check Time |
|---|---|---|---|
global |
Server-wide limit | global |
At receive |
domain |
Per sending domain | example.com |
At receive |
sender |
Per sender email | user@example.com |
At receive |
ip |
Per client IP | 192.168.1.1 |
At receive |
api_key |
Per API key | key-abc123 |
At receive |
recipient_domain |
Per recipient domain | gmail.com |
At send |
When a message is received (via SMTP or API), the first 5 levels are checked. If any limit is exceeded, the message is rejected.
Recipient domain limits are checked at send time (in the queue processor). If exceeded, the message is deferred (not rejected) and will be retried when the rate limit window resets.
rate_limit:
enabled: true
# Global server limits
global:
messages_per_hour: 50000
messages_per_day: 500000
# Default limits per sending domain
default_domain:
messages_per_hour: 1000
messages_per_day: 10000
# Default limits per sender email
default_sender:
messages_per_hour: 100
messages_per_day: 1000
# Default limits per client IP
default_ip:
messages_per_hour: 500
messages_per_day: 5000
# Default limits per API key
default_api_key:
messages_per_hour: 1000
messages_per_day: 10000
# Default limits per recipient domain (e.g., gmail.com, mail.ru)
# Checked at send time - if exceeded, message is deferred
default_recipient_domain:
messages_per_hour: 5000
messages_per_day: 50000
# Per-recipient-domain limits (override default)
recipient_domains:
gmail.com:
messages_per_hour: 1000
messages_per_day: 10000
mail.ru:
messages_per_hour: 1000
messages_per_day: 10000
yandex.ru:
messages_per_hour: 1000
messages_per_day: 10000Override default limits for specific domains:
domains:
example.com:
rate_limit:
messages_per_hour: 5000
messages_per_day: 50000
recipients_per_message: 100
newsletter.example.com:
rate_limit:
messages_per_hour: 10000
messages_per_day: 100000- Hourly counter: Resets every hour from when the first message was sent
- Daily counter: Resets every 24 hours from when the first message was sent
When a message is received, limits are checked in this order:
- Global limit
- Domain limit
- Sender limit
- IP limit
- API key limit
The first limit that is exceeded causes rejection. All counters for allowed messages are incremented atomically.
When a message is sent (from queue), recipient domain limits are checked:
- Recipient domain limit (for each recipient)
If the recipient domain limit is exceeded, the message is deferred (not rejected) and scheduled for retry.
Setting a limit to 0 means unlimited:
rate_limit:
global:
messages_per_hour: 0 # Unlimited hourly
messages_per_day: 100000 # But limited dailyRate limit counters are persisted to BoltDB and survive server restarts. The flush interval is configurable:
rate_limit:
flush_interval: 10s # Default: 10scurl http://localhost:8080/api/v1/ratelimits \
-H "Authorization: Bearer YOUR_API_KEY"Response:
{
"enabled": true,
"global": {
"messages_per_hour": 50000,
"messages_per_day": 500000
},
"default_domain": {
"messages_per_hour": 1000,
"messages_per_day": 10000
},
"domains": {
"example.com": {
"messages_per_hour": 5000,
"messages_per_day": 50000,
"recipients_per_message": 100
}
}
}Get current counter values for a specific level and key:
# Global stats
curl http://localhost:8080/api/v1/ratelimits/global/global \
-H "Authorization: Bearer YOUR_API_KEY"
# Domain stats
curl http://localhost:8080/api/v1/ratelimits/domain/example.com \
-H "Authorization: Bearer YOUR_API_KEY"
# Sender stats
curl http://localhost:8080/api/v1/ratelimits/sender/user@example.com \
-H "Authorization: Bearer YOUR_API_KEY"
# IP stats
curl http://localhost:8080/api/v1/ratelimits/ip/192.168.1.1 \
-H "Authorization: Bearer YOUR_API_KEY"
# API key stats
curl http://localhost:8080/api/v1/ratelimits/api_key/key-123 \
-H "Authorization: Bearer YOUR_API_KEY"
# Recipient domain stats
curl http://localhost:8080/api/v1/ratelimits/recipient_domain/gmail.com \
-H "Authorization: Bearer YOUR_API_KEY"Response:
{
"level": "domain",
"key": "example.com",
"hourly_count": 150,
"daily_count": 1200,
"hourly_limit": 5000,
"daily_limit": 50000,
"hour_start": "2024-01-15T10:00:00Z",
"day_start": "2024-01-15T00:00:00Z"
}curl -X PUT http://localhost:8080/api/v1/ratelimits/example.com \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages_per_hour": 2000,
"messages_per_day": 20000,
"recipients_per_message": 50
}'When rate limit is exceeded, API returns:
{
"error": "rate limit exceeded",
"denied_by": "sender",
"denied_key": "user@example.com",
"retry_after": 1800
}denied_by: Which level triggered the rejectiondenied_key: The specific key that was limitedretry_after: Seconds until the limit resets
# Rate limit rejections by level
rate(sendry_ratelimit_denied_total[5m])
# Current usage percentage
sendry_ratelimit_usage_ratio{level="global"}
Rate limit events are logged:
{"level":"warn","component":"ratelimit","msg":"rate limit exceeded","denied_by":"sender","key":"user@example.com","retry_after":"30m"}rate_limit:
enabled: true
global:
messages_per_hour: 100000
messages_per_day: 1000000
default_domain:
messages_per_hour: 10000
messages_per_day: 100000
default_sender:
messages_per_hour: 1000
messages_per_day: 10000rate_limit:
enabled: true
global:
messages_per_hour: 50000
default_domain:
messages_per_hour: 5000
# Relaxed sender limits for bulk sending
default_sender:
messages_per_hour: 5000
messages_per_day: 50000rate_limit:
enabled: true
global:
messages_per_hour: 10000
# Strict per-domain limits
default_domain:
messages_per_hour: 100
messages_per_day: 500
# Very strict per-sender limits
default_sender:
messages_per_hour: 20
messages_per_day: 100Limit outgoing emails to major mail providers to avoid being blocked:
rate_limit:
enabled: true
# Default for all recipient domains
default_recipient_domain:
messages_per_hour: 5000
messages_per_day: 50000
# Specific limits for major providers
recipient_domains:
gmail.com:
messages_per_hour: 1000
messages_per_day: 10000
mail.ru:
messages_per_hour: 1000
messages_per_day: 10000
yandex.ru:
messages_per_hour: 1000
messages_per_day: 10000
yahoo.com:
messages_per_hour: 1000
messages_per_day: 10000
outlook.com:
messages_per_hour: 1000
messages_per_day: 10000
hotmail.com:
messages_per_hour: 1000
messages_per_day: 10000rate_limit:
enabled: false