Read Convor rate-limit response headers and implement bounded retries without relying on obsolete plan-wide request tables.
Convor applies shared and route-specific limits to protect tenant data and platform availability. The effective ceiling can vary by route group, endpoint, organization plan, and caller type.
Do not hardcode one plan-wide requests-per-minute table. The response headers for the request you are making are the source of truth.
Rate-limited API routes return:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
X-RateLimit-Reset: 1785772860000| Header | Meaning |
|---|---|
X-RateLimit-Limit | Effective request ceiling for the current bucket and window. |
X-RateLimit-Remaining | Requests remaining in the current window. |
X-RateLimit-Reset | Unix timestamp in milliseconds when the current window resets. |
When a request is rejected with 429 Too Many Requests, the response also
contains:
Retry-After: 12Retry-After is the number of seconds to wait before retrying. Prefer it over a
locally calculated delay.
A rate-limit rejection uses the normal API error envelope:
{
"error": {
"code": "TOO_MANY_REQUESTS",
"message": "Too many requests. Please try again later.",
"details": {
"retryAfter": 12
},
"correlationId": "01J..."
}
}The details and correlationId fields are optional. Use the HTTP header as the
retry source even when the body also includes retryAfter.
Use bounded retries, respect Retry-After, and add jitter so many workers do not
retry at the same instant.
function sleep(milliseconds) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
async function convorRequest(url, options, maxRetries = 4) {
for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
const response = await fetch(url, options);
if (response.status !== 429 || attempt === maxRetries) {
return response;
}
const retryAfter = Number(response.headers.get("Retry-After"));
const fallbackSeconds = Math.min(30, 2 ** attempt);
const baseDelay = Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter * 1000
: fallbackSeconds * 1000;
const jitter = Math.floor(Math.random() * 250);
await sleep(baseDelay + jitter);
}
throw new Error("Unreachable");
}Do not blindly retry mutations
A 429 response means the rejected request was not accepted by the rate-limit
guard, but other network failures can occur after a mutation is processed. For
POST, PUT, PATCH, and DELETE, follow the endpoint's replay guarantees or
verify resource state before retrying.
429 responses.Last updated: Aug 3, 2026
Was this page helpful?
REST API Conventions
Use the Convor REST API safely with the correct base URL, response shapes, pagination models, error envelope, and retry rules.
Outbound Webhooks
Receive signed Convor event deliveries, verify the raw request body, deduplicate retries, and distinguish customer webhooks from provider callbacks.