Skip to main content

Rate Limiting

Allure Connect applies operation-specific limits where abuse or expensive work needs a hard boundary. There is no single global read/write/admin RPM table for every partner endpoint.

Partner API Limits

The default window is 60 seconds. Deployments can override these values with the corresponding CONNECT_*_RATE_LIMIT_PER_WINDOW environment variable.

Operation Default Scope
POST /api/v1/packages/upload-url 120/window API key
POST /api/v1/packages/process 60/window workspace
POST /api/v1/connectors/events 20/window API key
POST /api/v1/connectors/installations 10/window API key

Connector connection, authorization, validation, sync, rotation, and OAuth callback routes also have operation-specific limits. Use their response headers and the interactive OpenAPI reference as the current contract.

Response Headers

Gated partner routes return:

X-RateLimit-Limit: 120
X-RateLimit-Remaining: 119
X-RateLimit-Reset: 1788264060

X-RateLimit-Reset is a Unix epoch timestamp expressed as whole seconds. Upload and package-process 429 responses use a flat JSON body:

{
  "error": "Rate limit exceeded",
  "code": "RATE_LIMIT_EXCEEDED"
}

Those upload routes do not promise a Retry-After header on 429. Wait until X-RateLimit-Reset, add jitter, and retry the same idempotent operation. Some customer connector routes also send Retry-After; consume it when present.

Backend Behavior

The limiter uses an in-memory fixed window unless CONNECT_RATE_LIMIT_BACKEND=convex and a Convex target are configured. Convex provides a shared window across serverless instances. Anonymous abuse-prone surfaces require the durable Convex path and fail closed when its guarded backend is unavailable.

There is no Redis or Upstash rate-limit integration in Connect.

Client Pattern

async function requestWithRateLimitRetry(url: string, init: RequestInit) {
  const response = await fetch(url, init);
  if (response.status !== 429) return response;

  const resetSeconds = Number(response.headers.get('X-RateLimit-Reset'));
  const nowSeconds = Math.ceil(Date.now() / 1000);
  const delayMs = Number.isFinite(resetSeconds)
    ? Math.max(1_000, (resetSeconds - nowSeconds) * 1_000)
    : 5_000;

  await new Promise((resolve) => setTimeout(resolve, delayMs + Math.random() * 500));
  return fetch(url, init);
}

Do not bypass a workspace-scoped limit by creating more API keys. For package uploads, reuse the same upload ticket and idempotency identifiers when the endpoint contract permits a safe retry.

Backend Failures Are Different

A required durable rate-limit backend failure returns HTTP 503 with RATE_LIMIT_UNAVAILABLE. Transient storage or other Convex failures use BACKEND_UNAVAILABLE and may include Retry-After. Honor that delay only when the response also says it is retryable. Other unexpected failures use INTERNAL_ERROR.

See API key security and the error reference.