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 the quota is counted
The quota is counted per company and per endpoint.
Two consequences worth knowing:
- Your calls are not penalised by other partners' traffic. Your quota is your own.
- Calling from several servers neither splits nor multiplies your quota: what is counted is your company, not the originating IP address.
- Each endpoint has its own counter. Saturating
GET /casesdoes not consume the quota ofPATCH /cases/update.
On public endpoints that require no authentication (case sharing, images, media), the company cannot be identified: the quota there is not scoped per company. Those endpoints are sized accordingly, with noticeably higher limits.
Requests rejected for another reason (insufficient rights, unknown case…) also consume quota. 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 |
PATCH /certificall/api/cases/bulk-update | 10 (up to 100 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 burns more quota. - Watch
X-RateLimit-Remainingand space out your calls as it approaches zero. - Prefer bulk endpoints over loops. To update several cases in a row (for instance propagating a scheduled date), use bulk update: it counts as a single request, where a loop consumes 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('Quota still exceeded after several attempts');
}
Need a higher limit?
If your integration regularly hits these limits, get in touch: quotas can be adjusted to match your usage.