Developers / Guide
Rate limits
Foxli employs dual rate limiting windows to protect infrastructure performance and ensure equal resource availability for all creators and agencies.
Rate limit thresholds
Rate limits are counted per account across all of its API keys and all workspaces:
120Requests / Minute
Enforced on a 60-second sliding window. Ideal for real-time app interactions and burst scheduling.
10,000Requests / Day
Enforced per UTC calendar day. Resets at midnight UTC (00:00:00Z).
Rate limit headers
Every API response contains headers indicating your current quota consumption:
| Header | Type | Description |
|---|---|---|
X-RateLimit-Limit | integer | The maximum requests allowed in the current minute window (120). |
X-RateLimit-Remaining | integer | The number of remaining requests in the current minute window. |
X-RateLimit-Reset | integer | Unix epoch timestamp (in seconds) when the current rate limit window resets. |
Retry-After | integer | Sent exclusively on 429 responses. Number of seconds before requests will be accepted. |
HTTP 429 & Retry-After
When rate limits are exceeded, Foxli immediately responds with HTTP status 429 Too Many Requests:
HTTP 429 response
HTTP/1.1 429 Too Many Requests Content-Type: application/json Retry-After: 35 X-RateLimit-Limit: 120 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1730811635 { "error": { "code": "rate_limited", "message": "Too many requests. Please slow down and retry in 35 seconds.", "request_id": "req_88f921ab" } }
Exponential backoff code example
To handle 429 responses reliably, use exponential backoff with jitter and respect the Retry-After response header:
async function fetchWithRetry(url, options = {}, retries = 3) { for (let attempt = 0; attempt <= retries; attempt++) { const res = await fetch(url, options); if (res.status !== 429) { return res; } if (attempt === retries) { throw new Error("Foxli API: Max retries exceeded due to rate limit."); } // Read Retry-After header (seconds) or compute exponential backoff with jitter const retryAfter = res.headers.get("Retry-After"); const delayMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : Math.pow(2, attempt) * 1000 + Math.random() * 500; console.warn(`Rate limited (429). Retrying in ${Math.round(delayMs)}ms...`); await new Promise((resolve) => setTimeout(resolve, delayMs)); } }