Errors
Handle Circadify REST API errors — structured error responses, error codes, and retry strategies.
The Circadify API uses standard HTTP status codes and returns structured error responses to help you diagnose and handle failures.
Error Response Format
Most API errors follow this structure:
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded",
"retryable": true
}json| Field | Type | Description |
|---|---|---|
error | string | Machine-readable error code |
message | string | Human-readable description |
retryable | boolean | Whether the request can be retried |
Rate-limited and quota responses use HTTP 429 with the body above; the API does not emit Retry-After or X-RateLimit-* headers, so apply your own backoff.
Session result failures return the session status plus an error message so SDKs can map the failure to their client-side error type.
Error Codes
Authentication Errors (401)
| Code | Description | Resolution |
|---|---|---|
API_KEY_INVALID | The API key is missing, malformed, revoked, or expired | Check your key in the developer dashboard. Create a new key if needed. |
UNAUTHORIZED | Bearer token is missing or invalid | Re-authenticate and obtain a fresh token. |
All API key validation failures return the same API_KEY_INVALID code regardless of the specific reason (missing, revoked, expired, or malformed). This prevents attackers from enumerating valid keys.
Authorization Errors (403)
| Code | Description | Resolution |
|---|---|---|
FORBIDDEN | You do not have permission to access this resource | Verify the session belongs to your account. |
DEVELOPER_PENDING | Your account is awaiting approval | Wait for the approval email or contact support. |
DEVELOPER_SUSPENDED | Your account has been suspended | Contact support to resolve. |
DEVELOPER_NOT_VERIFIED | Your email address has not been verified | Check your inbox for the verification email. |
Request Errors (400)
| Code | Description | Resolution |
|---|---|---|
INVALID_REQUEST | Missing or invalid request parameters | Check the request body and path parameters against the endpoint documentation. |
VERIFICATION_TOKEN_INVALID | Email verification or password reset token is invalid or expired | Request a new verification email or password reset. |
Not Found (404)
| Code | Description | Resolution |
|---|---|---|
SESSION_NOT_FOUND | The session ID does not exist | Sessions expire after a short time. Create a new session. |
DEVELOPER_NOT_FOUND | No developer account found for this identifier | Check the account exists and the ID is correct. |
Conflict (409)
| Code | Description | Resolution |
|---|---|---|
EMAIL_ALREADY_EXISTS | An account with this email already exists | Log in with the existing account or use a different email. |
Gone (410)
| Code | Description | Resolution |
|---|---|---|
SESSION_EXPIRED | The session timed out before completion | Create a new session and retry the measurement. |
Processing Failed (422)
| Code | Description | Resolution |
|---|---|---|
failed | A completed upload could not be processed | Start a new measurement and retry under better scan conditions. |
Rate Limiting & Quota (429)
| Code | Description | Resolution |
|---|---|---|
RATE_LIMIT_EXCEEDED | Sandbox (ck_test_) request rate limit exceeded (best-effort) | Retryable. Back off with exponential delay and retry. See Rate Limits. |
QUOTA_EXCEEDED | Monthly scan quota exhausted on a production (ck_live_) key | Not retryable this month. Check the Usage page in the developer portal. |
Server Errors (500/503)
| Code | Description | Resolution |
|---|---|---|
INTERNAL_ERROR | An unexpected server error occurred | Retry with exponential backoff. If persistent, contact support. |
INFERENCE_FAILED | The measurement could not be processed | Retryable. Retry the full session. |
SERVICE_UNAVAILABLE | A backend service is temporarily unavailable | Retryable. Wait and retry with exponential backoff. |
HTTP Status Code Summary
| Status | Meaning |
|---|---|
200 | Success |
201 | Created (e.g., new API key) |
400 | Bad Request — invalid parameters |
401 | Unauthorized — invalid or missing credentials |
403 | Forbidden — insufficient permissions |
404 | Not Found — resource does not exist |
409 | Conflict — resource state conflict |
410 | Gone — resource has expired |
422 | Unprocessable Entity — uploaded measurement could not be processed |
429 | Rate Limited — too many requests |
500 | Internal Error — server-side failure |
503 | Service Unavailable — temporary backend issue |
Retry Strategy
For retryable errors (retryable: true), implement exponential backoff:
async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.ok) return response;
const body = await response.json();
// Honor the `retryable` flag: don't retry non-retryable client errors.
// Note a 429 can be QUOTA_EXCEEDED (retryable: false) — don't retry it.
if (response.status < 500 && body.retryable === false) {
throw new Error(body.message);
}
if (attempt === maxRetries) {
throw new Error(body.message);
}
// No Retry-After header is emitted — use exponential backoff
const delay = Math.min(1000 * Math.pow(2, attempt), 30000);
await new Promise(resolve => setTimeout(resolve, delay));
}
}typescriptRecommended parameters:
| Parameter | Value |
|---|---|
| Max retries | 3 |
| Initial delay | 1 second |
| Max delay | 30 seconds |
| Backoff multiplier | 2x |
Never retry 401 or 403 errors — these indicate a credentials or permissions issue that will not resolve with retries. A 429 may be RATE_LIMIT_EXCEEDED (sandbox, retryable) or QUOTA_EXCEEDED (production quota, not retryable) — branch on the retryable flag. No Retry-After header is emitted, so use your own backoff.
Next Steps
- Rate Limits — Understand and manage rate limits
- Sessions — Session endpoints and lifecycle
- SDK Error Codes — Client-side error reference