Rate Limiting
01 SOFTWARE API rate limit policies and optimization guide
Rate Limiting
The 01 SOFTWARE API enforces two types of limits to ensure fair usage and system stability.
- Rate Limit — Per-minute/per-hour request limits (same for all plans)
- Monthly Quota — Monthly total API call limits (varies by plan)
All limits are applied per tenant. Tenants are identified by the X-Client-Key header.
Rate Limit (Per-Minute/Per-Hour)
Applied equally to all plans to prevent system overload from burst requests.
| Type | Limit |
|---|---|
| Per-minute requests | 600 |
| Per-hour requests | 10,000 |
Response When Exceeded
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 45
X-RateLimit-Limit: 600
X-RateLimit-Window: minute{
"error": "Rate limit exceeded",
"message": "Too many requests. Per-minute limit (600) exceeded. Please try again later.",
"retryAfter": 45
}| Header | Description |
|---|---|
Retry-After | Wait time until next request (seconds) |
X-RateLimit-Limit | Maximum requests for the window |
X-RateLimit-Window | Applied limit window (minute or hour) |
Monthly Quota
The total monthly API call count varies by plan.
| Plan | Monthly API Calls |
|---|---|
| Free | 100,000 |
| Starter | 500,000 |
| Basic | 2,000,000 |
| Pro | 10,000,000 |
| Enterprise | Unlimited |
Usage Headers
Monthly usage information is included in response headers for successful requests.
HTTP/1.1 200 OK
X-Usage-Limit: 100000
X-Usage-Current: 45230
X-Usage-Remaining: 54770| Header | Description |
|---|---|
X-Usage-Limit | Maximum monthly API calls |
X-Usage-Current | Current month usage |
X-Usage-Remaining | Remaining calls this month |
Response When Exceeded
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-Usage-Limit: 10000
X-Usage-Current: 10001
X-Usage-Remaining: 0{
"error": "Usage limit exceeded",
"message": "Monthly API call limit (10,000) exceeded for your plan. Please upgrade your plan or wait until next month.",
"usage": {
"limit": 10000,
"current": 10001,
"remaining": 0
}
}Distinguishing 429 Responses
You can distinguish between the two types of 429 responses by their headers.
| Type | Header | Meaning |
|---|---|---|
| Rate Limit | X-RateLimit-Window present | Per-minute/per-hour limit exceeded - retry after a short wait |
| Monthly Quota | X-Usage-Limit present | Monthly quota exceeded - plan upgrade required |
Retry Strategy
Exponential Backoff
The SDK internally applies exponential backoff.
- Default max retries: 3
- Retryable status codes: 408, 429, 500, 502, 503, 504
- Non-retryable status codes: 401, 403, 404, 422
- Delay:
Math.min(1000 * 2^attempt, 10000)(1s, 2s, 4s)
Monthly API quota exceeded (429 + X-Usage-Limit header) is not retried and throws a UsageLimitError instead.
Manual Retry
async function fetchWithRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn()
} catch (error) {
if (error.status === 429 && i < maxRetries - 1) {
const retryAfter = error.headers['retry-after'] || (2 ** i)
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000))
continue
}
throw error
}
}
}Error Handling
SDK Error Handling
import { isUsageLimitError } from '@01.software/sdk'
try {
const { docs } = await client.from('products').find()
} catch (error) {
if (isUsageLimitError(error)) {
// Monthly API quota exceeded
console.log(error.message)
console.log(error.usage) // { used, limit } usage information
console.log(error.suggestion) // Plan upgrade guidance
showNotification('Monthly API call limit exceeded. Please consider upgrading your plan.')
}
}Optimization Strategies
1. Leverage Caching
// Automatic caching with React Query
const { data } = client.query.useQuery(
{
collection: 'products',
options: { limit: 10 },
},
{
staleTime: 5 * 60 * 1000, // 5 minutes
gcTime: 10 * 60 * 1000 // 10 minutes
}
)2. Batch Requests
// Bad: Multiple requests
for (const id of productIds) {
await client.from('products').findById(id) // N requests
}
// Good: Single request
const { docs } = await client.from('products').find({
where: {
id: { in: productIds }
}
})3. Request Only Needed Fields
// Use select option to request only needed fields
const { docs } = await client.from('products').find({
limit: 10,
select: { title: true, price: true }
})4. Use Webhooks
Use webhooks instead of polling when you need real-time updates.
// Bad: Polling (inefficient)
setInterval(async () => {
await client.from('orders').find()
}, 5000) // Request every 5 seconds
// Good: Webhook (efficient)
// app/api/webhook/route.ts
export async function POST(request: Request) {
return handleWebhook(request, async (event) => {
if (event.collection === 'orders') {
// Handle order updates
}
})
}5. Pagination Optimization
// Good: Use appropriate limit
const { docs } = await client.from('products').find({
limit: 20, // Only what you need
page: 1
})
// Bad: Excessive limit
const { docs } = await client.from('products').find({
limit: 1000 // Unnecessarily large amount of data
})Next Steps
- REST API - How to use the REST API
- GraphQL - How to use the GraphQL API
- Client - SDK client setup
- Error Handling - How to handle errors