Rate limits

Flow uses two rolling budgets per API key — per-minute and per-day — with response headers that let you adapt in real time.

Default limits

  • 60 requests per minute (fixed 60-second windows)
  • 10,000 requests per day (fixed UTC-midnight windows)

These are the defaults for a new data key. Higher limits can be configured per key — contact us if you expect sustained traffic above the defaults.

WebSocket connections don't consume REST rate budget while open. They do count against a separate concurrent-connection cap (default: 3 simultaneous connections per key).

Rate-limit headers

Every REST response (including 429) includes the current budget state:

http
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 pre-emptively slows down before hitting zero.

Handling 429 responses

If you exceed either budget, the API returns HTTP 429:

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

The response also includes a standard Retry-After header with the same value in seconds. Wait that duration before retrying. Exponential backoff on top of Retry-After is recommended 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');
}

Fairness

Each API key has its own counters — traffic from one key never affects another, even inside the same organization. Keys also can't burst across minute boundaries: at the start of each minute, the remaining budget resets to the full limit regardless of how many requests were left over.