Rate limits
The S4E API enforces rate limits to ensure fair usage and platform stability. This page documents default limits, per-endpoint overrides, response headers, and retry strategies.
Default Limits
| Tier | Requests per Minute | Burst Allowance |
|---|---|---|
| Standard | 100 | 20 |
| Premium | 1,000 | 100 |
| Enterprise | 10,000 | 500 |
The burst allowance permits short spikes above the per-minute rate. Burst tokens are replenished at the per-minute rate.
Per-Endpoint Limits
Some endpoints have tighter limits due to resource intensity:
| Endpoint | Limit | Notes |
|---|---|---|
POST /api/scan/create |
10 req/min | Scan creation is resource-heavy. |
POST /api/findings/export |
5 req/min | Export generates large files. |
POST /api/assets/bulk |
5 req/min | Bulk import processes many records. |
POST /api/user/auth/login |
10 req/min | Prevents brute-force attacks. |
GET /api/events/stream |
5 connections | Concurrent SSE connections. |
| All other endpoints | Tier default | See table above. |
Rate Limit Headers
Every API response includes rate limit headers:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests allowed in the current window. |
X-RateLimit-Remaining |
Requests remaining in the current window. |
X-RateLimit-Reset |
Unix timestamp when the window resets. |
429 Response
When the rate limit is exceeded, the API returns 429 Too Many Requests:
{
"status": "error",
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded. Retry after 23 seconds.",
"retry_after": 23
}
}
The Retry-After header is also set:
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1714305660
Retry Strategy
Implement exponential backoff with jitter to handle rate limits gracefully.
Python Example
import time
import random
import requests
def api_request(url, headers, max_retries=5):
for attempt in range(max_retries):
response = requests.get(url, headers=headers)
if response.status_code != 429:
return response
retry_after = int(response.headers.get("Retry-After", 60))
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")
response = api_request(
"https://api.s4e.io/api/findings",
headers={"Authorization": "Bearer YOUR_API_KEY"}
)
cURL with Retry
retry_count=0
max_retries=5
while [ $retry_count -lt $max_retries ]; do
response=$(curl -s -w "\n%{http_code}" \
"https://api.s4e.io/api/findings" \
-H "Authorization: Bearer YOUR_API_KEY")
http_code=$(echo "$response" | tail -1)
if [ "$http_code" != "429" ]; then
echo "$response" | head -n -1
break
fi
retry_count=$((retry_count + 1))
sleep_time=$((2 ** retry_count + RANDOM % 5))
echo "Rate limited. Retrying in ${sleep_time}s..."
sleep $sleep_time
done
SDK Handling
The S4E Python SDK handles rate limits automatically:
from s4e_sdk import S4EClient
client = S4EClient(
api_key="YOUR_API_KEY",
retry_on_rate_limit=True,
max_retries=5
)
findings = client.findings.list(severity="critical")
The SDK respects the Retry-After header and uses exponential backoff with jitter by default.
Monitoring Your Usage
Check your current rate limit status:
Review only the rate limit headers:
Rate Limit Exemptions
The following are not counted against your rate limit:
- Webhook deliveries from S4E to your endpoint.
- SSE event stream messages (the connection itself counts, but individual events do not).
- Health check endpoint (
GET /api/health).
Requesting Higher Limits
If your integration requires higher rate limits:
- Review your API usage patterns to ensure you are not making unnecessary calls.
- Implement caching for frequently accessed data.
- Use bulk endpoints where available (e.g.,
POST /api/assets/bulk). - Contact S4E support or your account manager to discuss Premium or Enterprise tier upgrades.
Tip
Use the If-Modified-Since header on GET requests to avoid fetching unchanged data. The API returns 304 Not Modified without consuming a rate limit token when the data has not changed.
Best Practices
| Practice | Recommendation |
|---|---|
| Monitor headers | Track X-RateLimit-Remaining to preemptively slow down. |
| Implement backoff | Use exponential backoff with jitter, not fixed delays. |
| Cache responses | Cache asset lists and finding counts to reduce API calls. |
| Batch requests | Use bulk endpoints for asset import and finding export. |
| Stagger requests | Distribute API calls evenly rather than bursting. |
Next Steps
- Authentication for setting up API access.
- API Keys for managing keys and tiers.