Rate Limiting
Rate Limiting and Performance
Tadabase enforces rate limits to ensure fair usage and system stability. Understanding and properly handling rate limits is crucial for building reliable integrations.
Rate Limit Tiers
Rate limits are tracked at two levels:
| Tier | Default Limit | Reset Period |
|---|---|---|
| Per Minute | 120-240 requests | 60 seconds |
| Per Day | 10,000+ requests | 24 hours |
Note
Rate limits may vary based on your subscription plan. Check your actual limits using the /status endpoint.
Checking Your Rate Limits
Status Endpoint
Check your current rate limit status:
GET https://api.tadabase.io/api/v1/status
Headers:
X-Tadabase-App-id: your_app_id
X-Tadabase-App-Key: your_app_key
X-Tadabase-App-Secret: your_app_secret
Response
{
"current_code": 200,
"api_key": {
"id": "4yQk924rgP",
"status": "Active",
"created_at": "2024-01-15 10:30:00"
},
"permissions": {
"read": true,
"create": true,
"update": true,
"delete": true,
"tasks": false,
"pages": false,
"imports": false,
"exports": false,
"logs": true,
"pdf_forms": false,
"pdf_pages": false
},
"per_minute_limit": {
"allowed": 240,
"remaining": 239,
"resets_in_seconds": 60,
"reset_timestamp": 1769585195
},
"per_day_limit": {
"allowed": 123456,
"remaining": 123441,
"resets_in_seconds": 72511,
"reset_timestamp": 1769657646
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
current_code |
integer | HTTP status code (200 for success) |
api_key.id |
string | Encoded API key identifier |
api_key.status |
string | API key status (Active, Inactive) |
api_key.created_at |
string | When the API key was created |
permissions |
object | Boolean flags for each permission type (read, create, update, delete, tasks, pages, imports, exports, logs, pdf_forms, pdf_pages) |
per_minute_limit.allowed |
integer | Maximum requests allowed per minute |
per_minute_limit.remaining |
integer | Requests remaining in current minute window |
per_minute_limit.resets_in_seconds |
integer | Seconds until the minute counter resets |
per_minute_limit.reset_timestamp |
integer | Unix timestamp when the minute counter resets |
per_day_limit.allowed |
integer | Maximum requests allowed per day |
per_day_limit.remaining |
integer | Requests remaining in current day window |
per_day_limit.resets_in_seconds |
integer | Seconds until the day counter resets |
per_day_limit.reset_timestamp |
integer | Unix timestamp when the day counter resets |
Checking Permissions
The /status endpoint now returns the permissions for your API key. Use this to verify what operations your API key can perform before making requests.
Rate Limit Response Headers
Every API response includes headers showing your current rate limit status:
Per-Minute Headers
| Header | Description | Example |
|---|---|---|
X-RateLimit-Limit |
Total requests allowed per minute | 120 |
X-RateLimit-Remaining |
Requests remaining this minute | 118 |
X-Retry-After |
Seconds until minute limit resets | 45 |
X-RateLimit-Reset |
Unix timestamp when limit resets | 1706380800 |
Per-Day Headers
| Header | Description | Example |
|---|---|---|
X-RateLimit-Daily-Limit |
Total requests allowed per day | 10000 |
X-RateLimit-Daily-Remaining |
Requests remaining today | 9850 |
X-Daily-Retry-After |
Seconds until day limit resets | 82800 |
X-Daily-RateLimit-Reset |
Unix timestamp when day resets | 1706468400 |
429 Rate Limit Exceeded Response
When you exceed a rate limit, you'll receive a 429 status code:
{
"type": "error",
"msg": "Too Many Attempts In Minute",
"status": 429
}
Or for daily limits:
{
"type": "error",
"msg": "Too Many Attempts In Day",
"status": 429
}
Implementing Rate Limit Handling
Basic Rate Limit Check
async function makeApiRequest(url, options) {
const response = await fetch(url, options);
// Check if rate limited
if (response.status === 429) {
const retryAfter = response.headers.get('X-Retry-After');
console.log(`Rate limited. Retry after ${retryAfter} seconds`);
// Wait and retry
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return makeApiRequest(url, options);
}
return response;
}
Proactive Rate Limit Monitoring
async function makeApiRequestWithRateLimitCheck(url, options) {
const response = await fetch(url, options);
// Check remaining requests
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
if (remaining setTimeout(resolve, 1000));
}
return response;
}
Exponential Backoff Strategy
async function makeApiRequestWithBackoff(url, options, maxRetries = 3) {
let attempt = 0;
while (attempt setTimeout(resolve, delay));
attempt++;
continue;
}
return response;
} catch (error) {
throw error;
}
}
throw new Error('Max retries exceeded');
}
Best Practices for Rate Limit Management
1. Monitor Rate Limit Headers
Always check the rate limit headers in responses:
const remaining = response.headers.get('X-RateLimit-Remaining');
const reset = response.headers.get('X-RateLimit-Reset');
console.log(`${remaining} requests remaining until ${new Date(reset * 1000)}`);
2. Implement Request Throttling
Limit the number of concurrent requests:
class RateLimitedQueue {
constructor(requestsPerMinute) {
this.maxRequests = requestsPerMinute;
this.queue = [];
this.activeRequests = 0;
this.requestTimes = [];
}
async enqueue(fn) {
// Wait if we've hit the rate limit
while (this.activeRequests >= this.maxRequests || this.isRateLimited()) {
await new Promise(resolve => setTimeout(resolve, 100));
}
this.activeRequests++;
this.requestTimes.push(Date.now());
try {
return await fn();
} finally {
this.activeRequests--;
}
}
isRateLimited() {
const oneMinuteAgo = Date.now() - 60000;
this.requestTimes = this.requestTimes.filter(time => time > oneMinuteAgo);
return this.requestTimes.length >= this.maxRequests;
}
}
// Usage
const queue = new RateLimitedQueue(120);
for (let i = 0; i makeApiRequest(`/records/${i}`));
}
3. Batch Operations When Possible
Instead of making individual requests, use batch operations:
// ❌ Bad: 100 individual requests
for (let i = 0; i
4. Cache Responses
Cache API responses to reduce unnecessary requests:
const cache = new Map();
async function getCachedData(url, options, ttl = 300000) {
const cacheKey = `${url}-${JSON.stringify(options)}`;
if (cache.has(cacheKey)) {
const { data, timestamp } = cache.get(cacheKey);
if (Date.now() - timestamp
5. Spread Out Requests
For scheduled tasks, spread requests throughout the minute:
const delayBetweenRequests = 60000 / 120; // ~500ms for 120 req/min
for (const item of items) {
await makeApiRequest(item);
await new Promise(resolve => setTimeout(resolve, delayBetweenRequests));
}
6. Use Async Formula Execution
For bulk operations, queue formula execution to improve response times:
POST /api/v1/data-tables/{tableId}/records
Headers:
X-Tadabase-Queue-Equation: 1
Body: { record data }
Performance Optimization Tips
Minimize Field Selection
// ❌ Fetches all fields
GET /api/v1/data-tables/{tableId}/records
// ✅ Only fetches needed fields (smaller response, faster)
GET /api/v1/data-tables/{tableId}/records?fields=id,name,email
Use Pagination Effectively
// ❌ Fetches default 100 records even if you only need 10
GET /api/v1/data-tables/{tableId}/records
// ✅ Only fetches what you need
GET /api/v1/data-tables/{tableId}/records?limit=10
Avoid Polling
Instead of polling for changes, use webhooks (when available) or implement smart polling with increasing intervals:
async function smartPoll(checkFn, maxAttempts = 10) {
let attempt = 0;
let delay = 1000;
while (attempt setTimeout(resolve, delay));
delay = Math.min(delay * 1.5, 60000); // Max 60 seconds
attempt++;
}
}
When to Upgrade Your Plan
Consider upgrading if you:
- Consistently hit daily rate limits
- Need to support high-traffic applications
- Run frequent bulk operations
- Have multiple integrations running concurrently
- Need real-time data synchronization
Complete Rate-Limited API Client Example
class TadabaseAPI {
constructor(appId, appKey, appSecret, maxRequestsPerMinute = 100) {
this.appId = appId;
this.appKey = appKey;
this.appSecret = appSecret;
this.maxRequestsPerMinute = maxRequestsPerMinute;
this.requestTimes = [];
}
async request(endpoint, options = {}) {
await this.throttle();
const url = `https://api.tadabase.io${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
'X-Tadabase-App-id': this.appId,
'X-Tadabase-App-Key': this.appKey,
'X-Tadabase-App-Secret': this.appSecret,
...options.headers
}
});
if (response.status === 429) {
const retryAfter = response.headers.get('X-Retry-After') || 60;
console.log(`Rate limited. Waiting ${retryAfter}s...`);
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
return this.request(endpoint, options);
}
this.logRateLimitStatus(response);
return response;
}
async throttle() {
const now = Date.now();
const oneMinuteAgo = now - 60000;
// Remove requests older than 1 minute
this.requestTimes = this.requestTimes.filter(time => time > oneMinuteAgo);
// If at limit, wait until oldest request is 1 minute old
if (this.requestTimes.length >= this.maxRequestsPerMinute) {
const oldestRequest = this.requestTimes[0];
const waitTime = 60000 - (now - oldestRequest);
await new Promise(resolve => setTimeout(resolve, waitTime));
}
this.requestTimes.push(now);
}
logRateLimitStatus(response) {
const remaining = response.headers.get('X-RateLimit-Remaining');
const reset = response.headers.get('X-RateLimit-Reset');
if (remaining
Next Steps
Learn how to handle errors and debug API issues:
→ Error Handling and Debugging
Master error handling, understand error codes, and learn debugging techniques.
We'd love to hear your feedback.