Help Center / api
Rate limits
Our API uses rate limiting to ensure fair usage and stability. Learn how limits work and how to handle them.
How the limit works
OSIR does not sell API tiers. Limits exist only to stop one caller starving the others, and they are deliberately generous for automated clients.
| Caller | Limit |
|---|---|
| Anonymous (no credentials) | 150 requests/minute per IP |
| Authenticated (bearer token) | 500 requests/minute per account |
Budgets refill continuously rather than resetting on a clock, so a burst that reaches the limit recovers within seconds. Health checks are never limited.
Two flows carry their own limits: AI chat relay 150/minute, and account signup 50/day per IP and 3/day per e-mail address.
Rate Limit Headers
Responses carry one header:
x-ratelimit-remaining: 149
| Header | Description |
|---|---|
| x-ratelimit-remaining | Requests left in your current budget |
A 429 additionally carries Retry-After and X-RateLimit-Retry-After, both in seconds.
Read x-ratelimit-remaining on every response and slow down as it approaches zero. X-RateLimit-Limit and X-RateLimit-Reset are not sent, so do not build logic that depends on them.
For automated clients
Availability results are cached briefly, so repeated checks of the same name never reach the registry. Anything that moves money accepts an Idempotency-Key, so retrying after a 429 or a timeout cannot double-charge you.
Handling Rate Limits
Check Headers
const response = await fetch('https://be.osir.com/v1/public/catalog/domains/example.com/availability')
const remaining = response.headers.get('x-ratelimit-remaining')
if (remaining < 10) {
// Slow down requests
}
Handle 429 Errors
async function fetchWithRetry(url, options, retries = 3) {
const response = await fetch(url, options)
if (response.status === 429 && retries > 0) {
const retryAfter = response.headers.get('Retry-After') || 60
await sleep(retryAfter * 1000)
return fetchWithRetry(url, options, retries - 1)
}
return response
}
429 Response
When rate limited. Honour the Retry-After header: retrying immediately spends budget you do not have.
{
"error": "Rate limit exceeded",
"retryAfterSeconds": 42
}
Best Practices
1. Implement Exponential Backoff
async function backoff(attempt) {
const delay = Math.min(1000 * Math.pow(2, attempt), 30000)
await sleep(delay)
}
2. Use Caching
// Cache responses that don't change often
const cache = new Map()
const CACHE_TTL = 60000 // 1 minute
async function getCachedDomains() {
if (cache.has('domains') && cache.get('domains').expires > Date.now()) {
return cache.get('domains').data
}
const data = await fetchDomains()
cache.set('domains', { data, expires: Date.now() + CACHE_TTL })
return data
}
3. Batch Requests
Instead of:
// Bad: 100 requests
for (const domain of domains) {
await checkDomain(domain)
}
Use:
// Good: 1 request
await checkDomains(domains) // Bulk endpoint
Increasing Limits
Need higher limits?
- Upgrade your plan
- Contact sales for enterprise pricing
- Apply for partner program