Rate Limits and CORS
Understand how Koji's API rate limiting works and how to configure CORS origins for your integration.
Rate Limits and CORS
Koji's API uses rate limiting to ensure fair usage and platform stability, and CORS validation to control which domains can make browser-based requests. This article explains how both systems work and how to handle them in your integration.
Rate Limiting
Rate limits protect the API from abuse and ensure consistent performance for all users. Every API request you make counts toward your rate limit allocation.
How Rate Limits Work
Rate limits are applied per API key. Each key has an allowance of requests within a rolling time window. When you exceed the limit, the API returns a 429 Too Many Requests response until the window resets.
The specific limits depend on your plan tier. Higher-tier plans receive more generous allowances to accommodate larger-scale integrations.
Rate Limit Headers
Every API response includes headers that tell you your current rate limit status:
| Header | Description |
|---|---|
X-RateLimit-Limit | The maximum number of requests allowed in the current window |
X-RateLimit-Remaining | How many requests you have left in the current window |
X-RateLimit-Reset | Unix timestamp when the current window resets |
Use these headers to implement intelligent rate limit handling in your application.
Example Response Headers
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1705312800
In this example, you have 87 requests remaining out of 100 for this window. The window resets at the timestamp 1705312800.
Handling 429 Responses
When you hit the rate limit, the API returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705312800
{
"error": "rate_limited",
"message": "Rate limit exceeded. Please retry after the indicated time."
}
The Retry-After header tells you how many seconds to wait before making another request.
Implementing Backoff
The recommended approach is exponential backoff with jitter:
async function apiRequestWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
if (attempt === maxRetries) {
throw new Error('Rate limit exceeded after max retries');
}
const retryAfter = parseInt(
response.headers.get('Retry-After') || '5',
10
);
const jitter = Math.random() * 1000;
const delay = (retryAfter * 1000) + jitter;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
The jitter prevents the "thundering herd" problem where multiple clients retry simultaneously after a rate limit window resets.
Proactive Rate Limit Management
Rather than waiting for 429 errors, monitor the X-RateLimit-Remaining header and slow down as you approach the limit:
async function managedApiRequest(url, options) {
const response = await fetch(url, options);
const remaining = parseInt(
response.headers.get('X-RateLimit-Remaining') || '100',
10
);
if (remaining < 10) {
// Getting close to the limit — add a delay
await new Promise(resolve => setTimeout(resolve, 1000));
}
return response;
}
CORS (Cross-Origin Resource Sharing)
CORS controls which domains are allowed to make browser-based requests to the Koji API. This is relevant if you are making API calls directly from client-side JavaScript (which we generally discourage for security reasons).
How CORS Works in Koji
Koji validates the Origin header on incoming requests against a list of allowed origins configured in your project settings. If the origin is not in the allowed list, the browser blocks the response.
Configuring Allowed Origins
- Go to your project's Settings > Integrations page.
- Find the CORS Origins section.
- Add each domain that needs to make browser-based requests.
- Include the full origin with protocol (e.g.,
https://yourapp.com). - Click Save.
CORS Configuration Guidelines
- Be specific. List exact origins rather than using wildcards.
- Include all environments. If you have staging and production domains, add both.
- Protocol matters.
https://yourapp.comandhttp://yourapp.comare different origins. - No trailing slashes. Use
https://yourapp.com, nothttps://yourapp.com/. - Subdomains are separate.
https://api.yourapp.comandhttps://yourapp.comare different origins.
Localhost for Development
During development, you can add http://localhost:3000 (or whatever port your dev server uses) to the allowed origins. Remove localhost origins before going to production.
When CORS Does Not Apply
CORS only affects browser-based requests. Server-to-server API calls (from your backend) are not subject to CORS restrictions. This is another reason to make API calls from your backend rather than your frontend.
Server-Side vs. Client-Side Requests
We strongly recommend making all API calls from your server rather than from client-side JavaScript:
| Concern | Client-Side | Server-Side |
|---|---|---|
| API key exposure | Key visible in browser | Key stays on your server |
| CORS configuration | Required | Not needed |
| Rate limit control | Hard to manage | Easy to manage |
| Security | Less secure | More secure |
If you must make client-side requests, use the embed widget instead — it handles authentication and CORS internally.
Preflight Requests
Browsers send an OPTIONS preflight request before making certain cross-origin requests. Koji handles preflight requests automatically based on your CORS configuration. Preflight requests do not count toward your rate limit.
Troubleshooting
"CORS policy: No 'Access-Control-Allow-Origin' header"
This means your domain is not in the allowed origins list. Add it in Settings > Integrations > CORS Origins.
"429 Too Many Requests"
You have exceeded your rate limit. Implement backoff logic and check the Retry-After header. If you consistently hit limits, consider upgrading your plan for higher allowances.
Preflight requests failing
Ensure your allowed origin exactly matches the Origin header the browser sends. Check protocol, domain, and port.
Next Steps
Related Articles
API Authentication
Learn how to authenticate with the Koji API using API keys and Bearer tokens.
Starting Interviews via API
Use the POST /start endpoint to programmatically launch interviews from your application.
Common Error Codes
Reference guide for error codes and messages you may encounter when using Koji.
Sending Messages via API
Understand how messages flow between your application and Koji during an API-started interview.
Completing Interviews via API
Use the POST /complete endpoint to finish an interview session and trigger automatic analysis.
Webhook Setup
Receive real-time notifications when interviews complete and analysis finishes using webhooks.