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.