Rate limits

X-RateLimit-Remaining-Minute · X-RateLimit-Remaining-Day · Retry-After

Two rolling budgets per workspace — per minute and per day — with headers on every response so a client can slow down before it is told to.

Budgets by plan

PlanPer minutePer day
Free6010,000
Pro30050,000
Scale1,000500,000
Organization600200,000
Organization (Enterprise)1,2002,000,000

The budget belongs to the workspace: every key it mints, and the console session, draw from the same pool. A key can be narrowed below its plan when it is minted. Your effective numbers are on GET /flow/v1/auth/me under current_key.

Not everything counts. Reading your own account (/auth/me, /auth/account/*) and AI Connect calls are exempt from the daily budget — BYOK is billed by the provider and managed calls by credits, and a request quota would be a second charge. They are still throttled per minute. Open WebSocket connections consume no REST budget; they count against a separate cap of 3 concurrent connections per key.

Headers

Every REST response, including a 429, carries the current state:

X-RateLimit-Limit-Minute: 60
X-RateLimit-Remaining-Minute: 42
X-RateLimit-Reset-Minute: 1745021580   # epoch seconds when the minute window resets
X-RateLimit-Limit-Day: 10000
X-RateLimit-Remaining-Day: 9847
X-RateLimit-Reset-Day: 1745049600

A well-behaved client reads X-RateLimit-Remaining-Minute and slows down before it reaches zero.

Handling 429

Over either budget, the API answers HTTP 429:

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Minute rate limit (60) exceeded",
    "details": {
      "retry_after_seconds": 23,
      "window": "minute"
    }
  }
}

The response also carries a standard Retry-After header with the same value in seconds. Wait that long before retrying; add exponential backoff on top for cascading overload.

Example: backoff

fetch wrapper with 429 handling
async function flowFetch(path, init = {}) {
  const url = `https://api.openmarkets.ai/flow/v1${path}`;
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch(url, {
      ...init,
      headers: {
        'X-API-Key': process.env.OPENMARKETS_API_KEY,
        ...init.headers,
      },
    });
    if (res.status !== 429) return res;

    const retryAfter = Number(res.headers.get('Retry-After') ?? 1);
    await new Promise((r) => setTimeout(r, retryAfter * 1000 * (attempt + 1)));
  }
  throw new Error('Rate limited after retries');
}

Windows

  • Fixed 60-second windows for the minute budget; fixed UTC-midnight windows for the day.
  • No bursting across a boundary: at the start of each minute the budget resets to the full limit.
  • Need more? Higher budgets come with the plan, or a per-key limit above the plan default on request. Plans are compared in the console.

Streams have their own limits — 200 contests per connection per channel, a 25-second heartbeat — on the Streaming page.