Rate Limiting¶
Craft Easy API uses Slowapi (built on top of limits) to enforce token-bucket rate limiting on all endpoints.
Configuration¶
| Setting | Default | Description |
|---|---|---|
RATE_LIMIT_ENABLED |
true |
Enable/disable rate limiting globally |
RATE_LIMIT_DEFAULT |
"100/minute" |
Default limit for all endpoints |
RATE_LIMIT_AUTH |
"10/minute" |
Stricter limit for authentication endpoints |
RATE_LIMIT_STORAGE_URI |
"memory://" |
Storage backend URI |
Rate Limit Key¶
The limiter identifies callers using a smart key function:
- Authenticated users: keyed by
user:{user_id}— ensures per-user limits regardless of IP - Anonymous requests: keyed by the client's IP address
This prevents one user behind a shared IP (e.g., corporate NAT) from exhausting the limit for everyone.
Storage Backends¶
| Backend | URI | Use Case |
|---|---|---|
| In-memory | memory:// |
Development, single-instance deployments |
| Redis | redis://host:6379 |
Production, multi-instance deployments |
For multi-instance deployments behind a load balancer, use Redis so all instances share the same rate limit counters.
Response on Limit Exceeded¶
When a client exceeds the rate limit, the API returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 42
X-Request-ID: abc-123
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded",
"retry_after": 42
}
The Retry-After header indicates when the client can retry (in seconds).
Per-Route Limits¶
You can apply custom limits to specific routes using Slowapi decorators:
from slowapi import Limiter
@app.get("/expensive-report")
@limiter.limit("5/minute")
async def expensive_report():
...
Auth Rate Limiting¶
Authentication endpoints (login, OTP, verify) have a stricter rate limit configured via RATE_LIMIT_AUTH. This protects against brute-force attacks:
Additional auth-specific protections are covered in Abuse Protection.