Rate Limits & Errors

Catalogian enforces rate limits per API key to ensure fair usage. This page covers limits by plan, how to detect rate limiting, and how to handle errors.

Rate limits

EndpointLimitWindow
REST API (authenticated)600 requestsPer minute, per API key
REST API (unauthenticated)100 requestsPer minute, per IP
MCP endpoint30 requestsPer minute, per API key
OpenAI Responses endpoint30 requestsPer minute, per API key

MCP calls also count toward the daily MCP limit on the Free plan (50 calls/day). Paid plans have unlimited daily MCP calls.

Rate limit headers

Every API response includes rate limit headers:

X-RateLimit-Limit: 600
X-RateLimit-Remaining: 594
X-RateLimit-Reset: 1710849600
HeaderDescription
X-RateLimit-LimitMax requests allowed in the current window
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets

Handling 429 Too Many Requests

When you exceed the rate limit, the API returns a 429 status:

HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json

{
  "error": "Rate limit exceeded. Try again in 12 seconds."
}

Use the Retry-After header to determine how long to wait before retrying. Implement exponential backoff for robust retry logic:

async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const res = await fetch(url, options);
    if (res.status !== 429) return res;

    const retryAfter = parseInt(res.headers.get("Retry-After") || "5");
    await new Promise(r => setTimeout(r, retryAfter * 1000));
  }
  throw new Error("Rate limit exceeded after retries");
}

Error codes

StatusMeaningCommon causes
400Bad RequestInvalid JSON body, missing required field, invalid query parameter
401UnauthorizedMissing or invalid API key
402Payment RequiredPlan limit reached (sources, SKUs, or features)
403ForbiddenAPI key lacks the required scope for this endpoint
404Not FoundSource, delta event, or resource doesn't exist (or belongs to another account)
429Too Many RequestsRate limit exceeded — see Retry-After header
500Internal Server ErrorUnexpected server error — contact support if persistent

Error response format

All errors return a JSON body with a single error field:

{
  "error": "Human-readable error message"
}

Error messages are safe to display to end users — they never contain internal details, stack traces, or other accounts' data.

Retry guidance

  • 429: Always retry with exponential backoff using the Retry-After header
  • 500: Retry up to 3 times with backoff — the issue may be transient
  • 400, 401, 403, 404: Do not retry — fix the request or credentials first
  • 402: Do not retry — upgrade your plan at Plans & Limits

Learn about cursor-based pagination. Pagination →