Skip to main content

Rate limits

The Certificall API caps the number of requests accepted over a rolling 60-second window. Beyond that, it responds with 429 Too Many Requests.

How requests are counted​

Requests are counted per company and per endpoint.

Three consequences worth knowing:

  • Your calls are not penalised by other partners' traffic. Your limit is your own.
  • Calling from several servers neither splits nor multiplies your limit: what is counted is your company, not the originating IP address.
  • Each endpoint has its own counter. Saturating GET /cases does not eat into the limit of PATCH /cases/update.

On public endpoints that require no authentication (case sharing, images, media), the company cannot be identified: requests there are not counted per company. Those endpoints are sized accordingly, with noticeably higher limits.

note

Requests rejected for another reason (insufficient rights, unknown case…) are counted too. A failing loop can therefore trigger a 429.

Limits per endpoint​

EndpointLimit (per minute)
POST /certificall/api/auth/token200
GET /certificall/api/cases5
POST /certificall/api/cases/bulk-create10 (up to 50 cases per call)
PATCH /certificall/api/cases/bulk-update10 (up to 100 cases per call)
POST /certificall/api/cases/bulk-delete10 (up to 50 cases per call)
POST /certificall/api/reports/report/:reportToken25
GET /certificall/api/reports/:reportToken100
GET /certificall/share/case/:shareToken180
GET /certificall/images/:imageUrl500
GET /certificall/medias/video/:videoUrl500
All other endpoints (including PATCH /certificall/api/cases/update)60

The 429 response​

Status code: 429 Too Many Requests

{
"statusCode": 429,
"message": "Rate limit exceeded: max 60 requests per 60s on this endpoint. Retry after 42s.",
"requestId": "b3f1c2e4-...",
"method": "PATCH",
"path": "/certificall/api/cases/update",
"timestamp": "2026-08-13T09:12:34.567Z",
"name": "ApiError",
"details": {
"header": "Error on request: b3f1c2e4-..."
}
}

Response headers:

HeaderMeaning
Retry-AfterNumber of seconds to wait before retrying
X-RateLimit-LimitRequests allowed within the window
X-RateLimit-RemainingRequests left (0 on a 429)
X-RateLimit-ResetSeconds before the counter resets

The X-RateLimit-* headers are also present on successful responses: you can watch X-RateLimit-Remaining to slow down before being blocked.

What to do​

  1. Honour Retry-After. Retrying immediately only adds requests to the counter.
  2. Watch X-RateLimit-Remaining and space out your calls as it approaches zero.
  3. Prefer bulk endpoints over loops. To create, update or delete several cases in a row, use bulk create, bulk update or bulk delete: each counts as a single request, where a loop counts as many as it holds cases. When no bulk endpoint fits your need, spread the requests across the window rather than sending them all at once.

Example of handling a 429:

async function callWithRetry(request, maxAttempts = 3) {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const response = await request();
if (response.status !== 429) return response;

const retryAfter = Number(response.headers.get('Retry-After')) || 60;
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
}
throw new Error('Rate limit still exceeded after several attempts');
}

Need a higher limit?​

If your integration regularly hits these limits, get in touch: they can be adjusted to match your usage.