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 /casesdoes not eat into the limit ofPATCH /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.
Requests rejected for another reason (insufficient rights, unknown case…) are counted too. A failing loop can therefore trigger a 429.
Limits per endpoint
| Endpoint | Limit (per minute) |
|---|---|
POST /certificall/api/auth/token | 200 |
GET /certificall/api/cases | 5 |
POST /certificall/api/cases/bulk-create | 10 (up to 50 cases per call) |
PATCH /certificall/api/cases/bulk-update | 10 (up to 100 cases per call) |
POST /certificall/api/cases/bulk-delete | 10 (up to 50 cases per call) |
POST /certificall/api/reports/report/:reportToken | 25 |
GET /certificall/api/reports/:reportToken | 100 |
GET /certificall/share/case/:shareToken | 180 |
GET /certificall/images/:imageUrl | 500 |
GET /certificall/medias/video/:videoUrl | 500 |
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:
| Header | Meaning |
|---|---|
Retry-After | Number of seconds to wait before retrying |
X-RateLimit-Limit | Requests allowed within the window |
X-RateLimit-Remaining | Requests left (0 on a 429) |
X-RateLimit-Reset | Seconds 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
- Honour
Retry-After. Retrying immediately only adds requests to the counter. - Watch
X-RateLimit-Remainingand space out your calls as it approaches zero. - 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.