New

Now in Claude, ChatGPT, Cursor & more with our MCP server

Back to docs
API Reference

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:

HeaderDescription
X-RateLimit-LimitThe maximum number of requests allowed in the current window
X-RateLimit-RemainingHow many requests you have left in the current window
X-RateLimit-ResetUnix 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

  1. Go to your project's Settings > Integrations page.
  2. Find the CORS Origins section.
  3. Add each domain that needs to make browser-based requests.
  4. Include the full origin with protocol (e.g., https://yourapp.com).
  5. 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.com and http://yourapp.com are different origins.
  • No trailing slashes. Use https://yourapp.com, not https://yourapp.com/.
  • Subdomains are separate. https://api.yourapp.com and https://yourapp.com are 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:

ConcernClient-SideServer-Side
API key exposureKey visible in browserKey stays on your server
CORS configurationRequiredNot needed
Rate limit controlHard to manageEasy to manage
SecurityLess secureMore 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