Skip to content

Rate Limits

Rate limits protect the API and ensure fair usage across all developers. The Circadify API enforces two types of limits: hourly request rate limits and monthly scan quotas.

Rate limits apply per developer account on a sliding 1-hour window:

PlanRequests per HourMonthly ScansMax API Keys
Starter3005003
Pro1,0005,0005
Enterprise5,00050,00010

Requests per hour limits apply to all API calls (session start, upload-complete, result retrieval). Monthly scans are consumed only when a new session is created via POST /sdk/session/start.

Every API response includes headers showing your current rate limit status:

X-RateLimit-Limit: 300
X-RateLimit-Remaining: 297
X-RateLimit-Reset: 1712005400
HeaderDescription
X-RateLimit-LimitMaximum requests allowed per hour
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the rate limit window resets

Session creation responses also include monthly usage headers:

X-Usage-Current: 42
X-Usage-Limit: 500
X-Usage-Remaining: 458
HeaderDescription
X-Usage-CurrentScans used this month
X-Usage-LimitMonthly scan limit for your plan
X-Usage-RemainingScans remaining this month

When you exceed the rate limit, the API returns a 429 response with a Retry-After header:

{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded",
"retryable": true
}

Response headers on 429:

Retry-After: 45
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1712005400

Always respect the Retry-After header — it tells you how many seconds to wait before retrying:

async function callWithRateLimit(url: string, options: RequestInit) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60', 10);
console.log(`Rate limited. Retrying in ${retryAfter} seconds.`);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return fetch(url, options);
}
return response;
}

When your monthly scan quota is exhausted, session creation returns a 429 with a different error code:

{
"error": "QUOTA_EXCEEDED",
"message": "Monthly scan quota exceeded",
"retryable": false
}

You can check your current usage at any time via the developer API:

Terminal window
curl https://api.circadify.com/developer/usage \
-H "Authorization: Bearer YOUR_TOKEN"

Response:

{
"current_month": {
"scan_count": 42
},
"limit": 500,
"remaining": 458,
"plan": "starter"
}

For historical usage:

Terminal window
curl https://api.circadify.com/developer/usage/history \
-H "Authorization: Bearer YOUR_TOKEN"

Response:

{
"history": [
{ "month": "2026-04", "scan_count": 42 },
{ "month": "2026-03", "scan_count": 387 },
{ "month": "2026-02", "scan_count": 215 }
]
}
  • Monitor remaining requests via X-RateLimit-Remaining headers to avoid hitting limits
  • Monitor remaining scans via X-Usage-Remaining to plan around monthly quotas
  • Implement backoff — never retry immediately on a 429 response
  • Cache results — once you retrieve session results, store them on your side. Don’t poll repeatedly for the same session
  • Use test keys (ck_test_*) during development to avoid consuming production quotas

If you consistently hit rate limits or quota limits, upgrade your plan in the developer dashboard or contact sales@circadify.com for enterprise pricing.