Overview

Rate Limits & Quotas

Limits are enforced per API key. Three dimensions apply: a request rate (per minute), a monthly quota (total calls), and the maximum top_k you can request per call.

How limits apply#

Limits are tracked per API key, not per IP — so your throughput is your own, independent of where requests originate. Each call to POST /v1/retrieve counts against both your rate limit and your monthly quota.

Your key also has a maximum top_k you can request. If you send a top_k above that maximum, the API clamps it down rather than rejecting the call.

Plan limits#

API access is paid; a key is provisioned by our team on one of the plans below. If your workload needs more, tell us your expected volume and we’ll raise the limits on your key.

PlanRate / minMonthly quotaMax top_k
Free201,00010
Pro120100,00025
Enterprise6002,000,00050
Paid access
The API is paid and there is no anonymous tier — a key is provisioned by our team. Contact us to get a key, and let us know your expected volume if you need limits raised.

When you hit a limit#

Exceeding your per-minute rate or your monthly quota returns 429 Too Many Requests in the standard error envelope. (An over-limit top_k is clamped instead, never rejected.)

429.json
{
  "success": false,
  "error": {
    "statusCode": 429,
    "message": "Rate limit exceeded",
    "error": "Too Many Requests",
    "path": "/v1/retrieve",
    "timestamp": "2026-05-30T12:00:00.000Z"
  }
}

Handling 429s#

Back off and retry with exponential backoff and jitter. A simple approach:

retry.ts
async function retrieveWithRetry(body, { maxRetries = 4 } = {}) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const res = await fetch("https://api.focusalpha.ai/v1/retrieve", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.FOCUSALPHA_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify(body),
    });

    if (res.status !== 429) return res;

    // exponential backoff with full jitter
    const delay = Math.random() * Math.min(1000 * 2 ** attempt, 15000);
    await new Promise((r) => setTimeout(r, delay));
  }
  throw new Error("Rate limit: retries exhausted");
}
Staying under the limit
Batch related questions where you can, cache results you’ll reuse, and scope queries with filters so each call does more useful work. If you consistently need more headroom, talk to us about a higher plan.