01 Software

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.

  1. Rate Limit — Per-minute/per-hour request limits (same for all plans)
  2. 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.

TypeLimit
Per-minute requests600
Per-hour requests10,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
}
HeaderDescription
Retry-AfterWait time until next request (seconds)
X-RateLimit-LimitMaximum requests for the window
X-RateLimit-WindowApplied limit window (minute or hour)

Monthly Quota

The total monthly API call count varies by plan.

PlanMonthly API Calls
Free100,000
Starter500,000
Basic2,000,000
Pro10,000,000
EnterpriseUnlimited

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
HeaderDescription
X-Usage-LimitMaximum monthly API calls
X-Usage-CurrentCurrent month usage
X-Usage-RemainingRemaining 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.

TypeHeaderMeaning
Rate LimitX-RateLimit-Window presentPer-minute/per-hour limit exceeded - retry after a short wait
Monthly QuotaX-Usage-Limit presentMonthly 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

On this page