Rate Limits & Error Handling
Understand API rate limits and build resilient integrations with proper error handling.
Rate Limits
The tersOS API enforces rate limits to ensure fair usage and platform stability. Standard rate limits apply to all API tokens, with exact limits depending on your plan.
| Plan | Requests / Minute | Daily Limit |
|---|---|---|
| Standard | 60 | 10,000 |
| Pro | 120 | 50,000 |
| Enterprise | Custom | Custom |
Rate Limit Headers
Every API response includes headers that indicate your current rate limit status:
X-RateLimit-Limit: 60X-RateLimit-Remaining: 54Retry-After: 30X-RateLimit-Limit— Maximum requests allowed per minuteX-RateLimit-Remaining— Requests remaining in the current windowRetry-After— Seconds to wait before retrying (only present on 429 responses)
Error Response Format
When an error occurs, the API returns a consistent JSON structure with success: false and an error message:
{ "success": false, "data": "The given data was invalid."}For validation errors (422), the response includes field-level details:
{ "success": false, "data": "The given data was invalid.", "errors": { "email": ["The email field is required."], "date": ["The date must be a future date."] }}Common Error Codes
| Code | Meaning | What to Do |
|---|---|---|
400 | Bad Request | The request was malformed or missing required parameters. |
401 | Unauthorized | Authentication failed. Token is missing, invalid, or revoked. |
403 | Forbidden | The token does not have permission for this action. |
404 | Not Found | The requested resource does not exist. |
422 | Validation Error | The request body failed validation. Check the error details. |
429 | Too Many Requests | Rate limit exceeded. Wait and retry using exponential backoff. |
500 | Server Error | An internal error occurred. Contact support if it persists. |
Best Practices
Exponential Backoff
When you receive a 429 or 5xx response, wait before retrying. Double the wait time with each attempt (1s, 2s, 4s, 8s...) to avoid overwhelming the server.
Cache Responses
Cache responses for data that does not change frequently (e.g., room lists, user profiles). This reduces API calls and improves your application's performance.
Use Pagination
List endpoints return paginated results. Use the page parameter to iterate through results rather than fetching everything at once.
Respect Retry-After
When the Retry-After header is present, always honor it. This tells you exactly how long to wait before sending another request.
Retry Example
Here is a complete example of an API client with exponential backoff and retry logic:
async function apiRequestWithRetry(url, token, maxRetries = 3) { for (let attempt = 0; attempt <= maxRetries; attempt++) { const response = await fetch(url, { headers: { 'Authorization': `Bearer ${token}`, 'Accept': 'application/json', }, });
if (response.status === 429) { // Rate limited — use exponential backoff const retryAfter = response.headers.get('Retry-After'); const delay = retryAfter ? parseInt(retryAfter, 10) * 1000 : Math.pow(2, attempt) * 1000;
console.warn(`Rate limited. Retrying in ${delay}ms...`); await new Promise((resolve) => setTimeout(resolve, delay)); continue; }
if (response.status >= 500 && attempt < maxRetries) { // Server error — retry with backoff const delay = Math.pow(2, attempt) * 1000; await new Promise((resolve) => setTimeout(resolve, delay)); continue; }
return response.json(); }
throw new Error('Max retries exceeded');}Always set a maximum number of retries to prevent infinite loops. Three retries with exponential backoff is a reasonable default for most use cases.