Skip to content

Rate Limits

IndepAI uses rate limiting to ensure fair usage and protect the API from abuse. This page explains how rate limits work and how to handle them in your application.

Rate limits are based on your subscription tier:

Tier Daily Limit AI Coach Limit Burst Limit
Starter 1,000 requests 50 requests 60 req/minute
Pro 10,000 requests 500 requests 300 req/minute

Unauthenticated requests are rate-limited by IP address:

  • 100 requests per day across all public endpoints
  • 10 requests per minute burst limit

Every API response includes rate limit headers:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1705312800
X-RateLimit-Resource: api
Header Description
X-RateLimit-Limit Maximum requests allowed in the period
X-RateLimit-Remaining Requests remaining in current period
X-RateLimit-Reset Unix timestamp when the limit resets
X-RateLimit-Resource The resource being rate-limited

When you exceed the rate limit, the API returns:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 3600
{
"success": false,
"error": "Rate limit exceeded",
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Upgrade to increase your API limit.",
"retryAfter": 3600
}

The Retry-After header tells you how many seconds to wait:

async function makeRequest() {
const response = await fetch("/api/v1/calculator", {
method: "POST",
body: JSON.stringify(data),
});
if (response.status === 429) {
const retryAfter = response.headers.get("Retry-After") || 60;
console.log(`Rate limited. Retry in ${retryAfter} seconds`);
// Wait and retry
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
return makeRequest();
}
return response.json();
}

For production applications, use exponential backoff:

async function fetchWithRetry(url: string, options: RequestInit, 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") || "60");
const backoff = Math.min(retryAfter, Math.pow(2, attempt) * 1000);
console.log(`Rate limited. Retrying in ${backoff}ms (attempt ${attempt + 1})`);
await new Promise((resolve) => setTimeout(resolve, backoff));
}
throw new Error("Max retries exceeded");
}

Track your remaining requests and plan accordingly:

let remainingRequests = 100;
async function makeAPICall() {
const response = await fetch("/api/v1/calculator", { method: "POST", body });
// Update tracking
remainingRequests = parseInt(response.headers.get("X-RateLimit-Remaining") || "0");
if (remainingRequests < 10) {
console.warn(`Low on API requests: ${remainingRequests} remaining`);
}
return response.json();
}

Avoid unnecessary API calls by caching:

const cache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
async function getCachedResult(key: string, fetcher: () => Promise<any>) {
const cached = cache.get(key);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return cached.data;
}
const data = await fetcher();
cache.set(key, { data, timestamp: Date.now() });
return data;
}

Where possible, batch multiple operations:

// Instead of multiple individual requests
const results = await Promise.all(
assets.map((a) =>
fetch("/api/v1/assets", {
method: "POST",
body: JSON.stringify(a),
})
)
);
// Consider using batch endpoints when available
const result = await fetch("/api/v1/assets/batch", {
method: "POST",
body: JSON.stringify({ assets }),
});

Some endpoints have additional restrictions:

Endpoint Additional Limit
/api/v1/ai/* 5-500/day by tier
/api/v1/geo/recommendations 20/hour
/api/v1/tax/calculate 50/hour

In development, you can test rate limit handling:

Terminal window
# Make 101 requests quickly to trigger limit
for i in {1..101}; do
curl -s -o /dev/null -w "%{http_code}\n" \
-X POST http://localhost:3000/api/v1/calculator \
-H "Content-Type: application/json" \
-d '{"currentAge":30,"annualIncome":80000,"annualExpenses":40000,"currentSavings":100000}'
done

If you need higher limits, upgrade your plan:

  • Starter - Standard API access for personal use
  • Pro - Higher limits for power users and integrations

See current pricing and upgrade at indepai.app/dashboard/settings.

Each HTTP request to an API endpoint counts as one request, regardless of the response status.

Yes, all requests count including 4xx and 5xx errors.

Daily limits reset at midnight UTC. The exact reset time is in the X-RateLimit-Reset header.

Contact support@indepai.app to discuss options.