The API enforces rate limits to ensure fair usage and protect infrastructure. Limits are applied per merchant for authenticated requests and per IP address for unauthenticated requests.
Rate limit tiers
| Tier | Scope | Limit | Window |
|---|
| Authenticated | Per merchant | 1,500 requests | 1 minute |
| Sensitive endpoints | Per merchant | 10 requests | 1 minute |
| Unauthenticated | Per IP address | 100 requests | 1 minute |
Sensitive endpoints
The following endpoints have stricter rate limits due to their security-sensitive nature:
/v1/api_keys — API key management
Every API response includes headers to help you track your usage:
| Header | Description |
|---|
X-RateLimit-Limit | Maximum requests allowed in the current window |
X-RateLimit-Remaining | Requests remaining in the current window |
Retry-After | Seconds to wait before retrying (only on 429 responses) |
Rate limit error response
When you exceed the rate limit, the API returns a 429 status code:
{
"error": {
"type": "rate_limit_error",
"code": "rate_limit_exceeded",
"message": "Too many requests. Please retry after the period specified in the Retry-After header.",
"param": null,
"doc_url": "https://docs.leanrails.com/guides/rate-limiting"
}
}
Handling rate limits
Implement exponential backoff with jitter to handle rate limit responses gracefully:
async function requestWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
const retryAfter = parseInt(response.headers.get("Retry-After") || "1", 10);
const jitter = Math.random() * 0.5;
const delay = (retryAfter + jitter) * 1000;
console.warn(`Rate limited. Retrying in ${delay / 1000}s...`);
await new Promise((resolve) => setTimeout(resolve, delay));
}
throw new Error("Max retries exceeded");
}
Best practices
Distribute requests evenly. Avoid sending large bursts of requests. Spread API calls over time to stay within limits.
- Cache responses when possible to reduce unnecessary API calls
- Use exponential backoff with jitter to avoid thundering herd problems when retrying
- Monitor rate limit headers to proactively throttle before hitting limits
- Use idempotency keys so retried requests don’t create duplicate resources