Rate Limiting
SpatialFlow enforces rate limits to ensure fair usage and platform stability. Rate limiting operates at two layers: middleware-level (global, per-request) and decorator-level (per-endpoint).
Rate Limit Tiers
All API requests are subject to global rate limits based on the authentication method used:
| Authentication | Limit | Period | Scope |
|---|---|---|---|
| API Key | 1,000 requests | Per hour | Per key |
| JWT Token | 1,000 requests | Per hour | Per token |
| Session (Admin) | 1,000 requests | Per hour | Per user |
| Unauthenticated | 100 requests | Per hour | Per IP |
Endpoint-Specific Limits
Certain endpoints have stricter per-minute limits to prevent abuse:
| Endpoint | Limit | Scope |
|---|---|---|
| Login | 5 requests per minute | Per IP |
| Token Refresh | 10 requests per minute | Per user |
| General API | 1,000 requests per hour | Per API key |
Rate Limit Headers
When a rate limit is exceeded, the API returns a 429 Too Many Requests response with the following headers:
| Header | Description |
|---|---|
Retry-After | Seconds until the rate limit resets (per RFC 7231) |
X-Quota-Status | Quota status when approaching limits (warning, alert, soft_throttle) |
X-Quota-Usage-Percent | Current event quota usage as a percentage |
Example 429 response with headers:
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 3600
X-Request-ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Rate Limit Response Format
When a global rate limit is exceeded, the API returns the following JSON body:
{
"detail": "Rate limit exceeded",
"error_code": "RATE_LIMIT_EXCEEDED",
"details": {
"limit": 1000,
"period_seconds": 3600
}
}
The details object includes the limit that was exceeded and the rate limit window in seconds.
Event Quota Throttling
In addition to request rate limits, SpatialFlow enforces event quotas based on your subscription plan. The quota system applies to tracked endpoints (device ingestion, geofence checks) and operates at these levels:
| Usage | Status | Behavior |
|---|---|---|
| < 100% | OK | Normal operation |
| 100% | WARNING | Warning headers added to responses |
| 110% | ALERT | Alert headers added, notification email sent |
| 120% | SOFT_THROTTLE | Rate limited to 50% capacity (429 responses) |
| 150% | HARD_CAP | All tracked requests blocked (403 responses) |
When soft-throttled (120%), the response uses a different error code:
{
"detail": "Event quota exceeded (120%). Rate limited.",
"error_code": "RATE_LIMITED",
"retry_after": 60
}
When hard-capped (150%), the API returns a 403 Forbidden:
{
"detail": "Event quota exceeded (150%). Please upgrade your plan.",
"error_code": "QUOTA_EXCEEDED"
}
Once your event usage reaches 150%, all tracked API requests are blocked entirely. Monitor the X-Quota-Usage-Percent header and upgrade your plan before reaching this threshold.
Handling Rate Limits
Check the Retry-After Header
Always read the Retry-After header from 429 responses before retrying. This header tells you exactly how many seconds to wait.
Implement Exponential Backoff
For transient rate limits, use exponential backoff with jitter to avoid thundering herd problems:
import time
import random
import requests
def call_api_with_retry(url, headers, max_retries=5):
"""Call the SpatialFlow API with automatic retry on rate limits."""
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code != 429:
return response
# Use Retry-After header if available
retry_after = int(response.headers.get("Retry-After", 60))
# Add jitter to avoid thundering herd
jitter = random.uniform(0, retry_after * 0.1)
wait_time = retry_after + jitter
print(f"Rate limited. Retrying in {wait_time:.1f}s (attempt {attempt + 1})")
time.sleep(wait_time)
raise Exception("Max retries exceeded")
# Usage
response = call_api_with_retry(
"https://api.spatialflow.io/api/v1/geofences",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
Do not guess how long to wait. The Retry-After header provides the exact number of seconds until your rate limit resets. Always prefer this value over a hardcoded delay.
Testing Rate Limits
You can observe rate limit behavior with a simple curl request:
# Send a request and inspect response headers
curl -i https://api.spatialflow.io/api/v1/geofences \
-H "Authorization: Bearer YOUR_API_KEY"
# When rate limited, you'll see:
# HTTP/1.1 429 Too Many Requests
# Retry-After: 3600
# Content-Type: application/json
#
# {"detail":"Rate limit exceeded","error_code":"RATE_LIMIT_EXCEEDED","details":{"limit":1000,"period_seconds":3600}}
Further Reading
- Error Reference -- Full list of error codes including
RATE_LIMIT_EXCEEDEDandQUOTA_EXCEEDED - Best Practices -- Production patterns for retry logic and connection management