Default limits
Two global limits apply to every endpoint that does not have a more specific limit configured:| Window | Limit |
|---|---|
| Per day | 200 requests |
| Per hour | 50 requests |
RATELIMIT_DEFAULT environment variable:
Endpoint-specific limits
Some endpoints apply stricter per-hour limits on top of the global defaults. The more restrictive limit always takes precedence.Authentication endpoints
Authentication endpoints
| Endpoint | Limit | Reason |
|---|---|---|
POST /api/auth/register | 5 per hour | Prevents automated account creation |
POST /api/auth/login | 10 per hour | Slows down brute-force attempts |
POST /api/auth/change-password | 3 per hour | Limits password-guessing attacks |
Task endpoints
Task endpoints
| Endpoint | Limit | Reason |
|---|---|---|
POST /api/tasks | 50 per hour | Prevents bulk task spam |
Tag endpoints
Tag endpoints
| Endpoint | Limit | Reason |
|---|---|---|
POST /api/tags | 20 per hour | Limits tag proliferation |
Read endpoints (
GET) and update/delete endpoints (PUT, PATCH, DELETE) are covered only by the global defaults, not endpoint-specific limits.Rate limit response
When a client exceeds any limit, the API responds with HTTP429 Too Many Requests. Flask-Limiter’s default error body is returned:
Rate limit headers
WhenRATELIMIT_HEADERS_ENABLED=true (the default), the API includes informational headers on every response:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum number of requests allowed in the current window |
X-RateLimit-Remaining | Number of requests remaining before the limit is hit |
X-RateLimit-Reset | Unix timestamp when the current window resets |
Retry-After | Seconds to wait before retrying (present only on 429 responses) |
Configuration
All rate limiting settings are controlled via environment variables:Enable or disable rate limiting globally. Set to
false in the testing configuration. Defaults to true.Backend used to store rate limit counters. Defaults to
memory:// (in-process, not shared across workers). For production deployments with multiple Gunicorn workers, use a Redis URL to share counters.Semicolon-separated list of default limits applied to every endpoint. Each limit uses the format
N per period where period is second, minute, hour, or day.Handling 429 responses
Implement retry logic with exponential backoff to gracefully handle rate limit errors. Check theRetry-After header when present to determine exactly how long to wait.
Best practices
Cache responses
Cache
GET responses locally to reduce the number of requests you make. List endpoints support filtering — request only the data you need.Use refresh tokens
Access tokens expire after 1 hour. Use the refresh token flow (
POST /api/auth/refresh) instead of logging in repeatedly — the refresh endpoint has no custom rate limit.Batch updates
Update task fields in a single
PUT /api/tasks/{id} call rather than making multiple PATCH requests. Include all changed fields in one request body.Monitor headers
Read
X-RateLimit-Remaining on every response. When it drops to single digits, slow down your request rate proactively rather than waiting to hit a 429.