Skip to content

Rate Limits & Quotas

Chronary enforces two types of limits to ensure fair usage and platform stability: per-key rate limits and monthly quotas.

The per-key rate limit depends on your plan:

| Plan | Rate limit | |------|-----------| | Free | 10 requests/second | | Pro | 50 requests/second | | Custom | Negotiated |

Requests that exceed this limit receive a 429 response with a Retry-After header indicating how many seconds to wait.

{
"error": {
"type": "rate_limit_error",
"message": "Rate limit exceeded. Retry after 1 second.",
"param": null,
"request_id": "req_abc123"
}
}

The Retry-After response header contains the number of seconds to wait before retrying.

Monthly quotas reset automatically at the start of each billing cycle. Exceeding a quota returns a 429 response with "type": "quota_exceeded".

Metric Free Pro Custom
Agents 350 Custom
Calendars 10250 Custom
iCal Subscriptions 5100 Custom
Webhook Endpoints 325 Custom
Per-Agent API Keys Not available50 Custom
Events created 2,500/mo125,000/mo Custom
API Calls 50,000/mo1,000,000/mo Custom
Webhook Deliveries 5,000/mo250,000/mo Custom
Availability Queries 10,000/mo1,000,000/mo Custom
Scheduling Proposals Not availableUnlimited Custom

v1 endpoints share most existing budgets rather than introducing new per-endpoint limits:

| Operation | Budget consumed | |-----------|-----------------| | POST /v1/calendars/:cal_id/events with status: "hold" | Events created (shared with regular events) | | PUT /v1/events/:id/confirm — promoting a hold | API Calls only (no extra event charge) | | PUT /v1/events/:id/release — releasing a hold | API Calls only | | GET /v1/calendars/:id/context | API Calls | | PUT / GET / DELETE /v1/calendars/:id/availability-rules | API Calls | | POST /v1/scheduling/proposals | Scheduling Proposals + API Calls | | POST /v1/scheduling/proposals/:id/respond | API Calls | | POST /v1/scheduling/proposals/:id/resolve | Events created (resolution creates a confirmed event) + API Calls |

Every request also consumes one API Call regardless of endpoint.

When querying availability across multiple agents (e.g., finding a common free slot), additional limits apply:

| Parameter | Free | Pro | Custom | |-----------|------|-----|--------| | Max agents per query | 5 | 20 | Custom | | Max date range | 30 days | 90 days | Custom |

Call GET /v1/usage to see your current consumption against your plan limits:

Terminal window
curl https://api.chronary.ai/v1/usage \
-H "Authorization: Bearer chr_sk_your_key_here"

Example response:

{
"plan": "free",
"period_start": "2026-04-01T00:00:00.000Z",
"period_end": "2026-04-30T23:59:59.000Z",
"agents": { "used": 3, "limit": 3 },
"calendars": { "used": 7, "limit": 10 },
"events": { "used": 1842, "limit": 2500 },
"api_calls": { "used": 12450, "limit": 50000 },
"webhooks": { "used": 956, "limit": 5000 },
"availability_queries": { "used": 304, "limit": 10000 },
"ical_subscriptions": { "used": 2, "limit": 5 },
"proposals": { "used": 0, "limit": 0 }
}

When you receive a 429 response, implement exponential backoff with jitter to avoid thundering-herd problems.

async function fetchWithRetry(url, options, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
// Use Retry-After header if present, otherwise calculate backoff
const retryAfter = response.headers.get('Retry-After');
const baseDelay = retryAfter
? parseFloat(retryAfter) * 1000
: Math.min(1000 * Math.pow(2, attempt), 30000);
// Add jitter: random delay between 0% and 100% of base delay
const jitter = Math.random() * baseDelay;
const delay = baseDelay + jitter;
console.log(`Rate limited. Retrying in ${Math.round(delay)}ms (attempt ${attempt + 1}/${maxRetries})`);
await new Promise((resolve) => setTimeout(resolve, delay));
}
throw new Error('Max retries exceeded');
}

When a webhook delivery fails (non-2xx response or timeout), Chronary retries with exponential backoff. The number of retry attempts depends on your plan:

| Plan | Retry attempts | Schedule (after each failure) | |------|----------------|-------------------------------| | Free | 4 | +1m → +5m → +30m | | Pro | 8 | +1m → +5m → +30m → +1h → +2h → +6h → +12h | | Custom | Negotiated | — |

Retries are scheduled at the plan in effect when the delivery was first enqueued; mid-flight plan downgrades preserve the longer schedule.

Quotas reset automatically at the start of each billing period — no manual action is required. You can check your current period and usage at any time with GET /v1/usage.