Rate Limits
Understand request quotas for the Unduit API and how to handle rate limit responses in your integration.
60 requests per minute
The same limit applies to every /api/v1 endpoint, including login and refresh-token.
Overview
The API enforces a global rate limit per client IP address. All routes under /api/v1 share one counter — authenticated and unauthenticated requests count toward the same limit.
| Policy | Value |
|---|---|
| Limit | 60 requests |
| Window | 1 minute (rolling) |
| Scope | All /api/v1/* endpoints |
| Key | Client IP address |
Response headers
Successful and rate-limited responses include standard rate-limit headers so you can track usage and backoff before hitting the cap.
| Header | Description |
|---|---|
| RateLimit-Limit | Maximum requests allowed per window (60). |
| RateLimit-Remaining | Requests remaining in the current window. |
| RateLimit-Reset | UNIX timestamp when the window resets. |
429 Too Many Requests
When the limit is exceeded, the API returns HTTP 429 with a JSON body:
{
"message": "Too many requests. Please try again later."
}Check RateLimit-Reset or use exponential backoff before retrying. Do not retry immediately in a tight loop.
Best practices
- Use pagination instead of firing many parallel list requests.
- Cache responses where appropriate (e.g. reference data, wallet balance) to reduce call volume.
- Serialize bulk operations — spread imports or sync jobs over time rather than bursting hundreds of requests in one second.
- Read
RateLimit-Remainingfrom response headers and slow down proactively when it approaches zero.
Example: handling 429
async function fetchWithBackoff(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
const reset = response.headers.get('RateLimit-Reset');
const waitMs = reset
? Math.max((Number(reset) * 1000) - Date.now(), 1000)
: Math.min(1000 * 2 ** attempt, 30000);
await new Promise((r) => setTimeout(r, waitMs));
}
throw new Error('Rate limit exceeded after retries');
}