Errors & Rate Limits¶
Consistent error handling, rate limiting, and pagination across all API endpoints.
Error Response Format¶
All errors return a JSON envelope with success: false:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "The prompt field is required",
"details": {
"prompt": ["The prompt field is required."]
}
}
}
Error Codes¶
| Code | HTTP Status | Description | Common Cause |
|---|---|---|---|
UNAUTHORIZED |
401 | Invalid or missing credentials | Bad API key, expired OAuth token |
FORBIDDEN |
403 | Valid credentials, insufficient permissions | Wrong scope, resource owned by another user |
NOT_FOUND |
404 | Resource does not exist | Wrong ID, deleted resource |
VALIDATION_ERROR |
422 | Invalid request parameters | Missing required field, wrong type |
RATE_LIMITED |
429 | Too many requests | Exceeded plan limits |
PRECONDITION_FAILED |
412 | Missing required precondition | Missing conver_id, empty prompt |
NO_CREDITS |
419 | Insufficient credit balance | Credit quota exhausted |
SERVER_ERROR |
500 | Internal server error | Upstream AI provider failure |
Rate Limiting¶
Limits by Plan¶
| Plan | Requests/Minute | Requests/Day |
|---|---|---|
| Starter | 60 | 1,000 |
| Business | 300 | 10,000 |
| Enterprise | 1,000 | 100,000 |
The Partner API has a dedicated throttle:partner rate limiter applied to all /api/v1/partners/* routes.
Rate Limit Headers¶
Every response includes rate limit headers:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests per window |
X-RateLimit-Remaining |
Requests remaining in current window |
X-RateLimit-Reset |
Unix timestamp when the window resets |
When Rate Limited¶
You receive a 429 Too Many Requests response:
{
"success": false,
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Retry after 45 seconds.",
"retry_after": 45
}
}
Retry Strategy¶
Use exponential backoff with jitter:
import time
import random
import requests
def api_call_with_retry(url, headers, max_retries=4):
for attempt in range(max_retries):
resp = requests.get(url, headers=headers)
if resp.status_code != 429:
return resp
wait = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait)
return resp # Return last response after all retries
async function apiCallWithRetry(url, headers, maxRetries = 4) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const resp = await fetch(url, { headers });
if (resp.status !== 429) return resp;
const wait = 2 ** attempt + Math.random();
await new Promise((r) => setTimeout(r, wait * 1000));
}
}
Pagination¶
List endpoints return paginated results using cursor-based or offset pagination:
Request Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
integer | 1 |
Page number |
per_page |
integer | 20 |
Items per page (max: 100) |
Response Metadata¶
{
"success": true,
"data": [ ... ],
"meta": {
"page": 1,
"per_page": 20,
"total": 85,
"current_page": 1,
"last_page": 5,
"from": 1,
"to": 20
}
}
Iterating Through Pages¶
Filtering and Sorting¶
List endpoints support query-parameter-based filtering. Common patterns:
| Pattern | Example | Description |
|---|---|---|
| Exact match | ?status=active |
Filter by exact value |
| Date range | ?created_after=2026-01-01 |
Filter by date |
| Sort | ?sort=created_at&direction=desc |
Sort results |
| Search | ?search=cloud+security |
Full-text search |
Specific filter options are documented on each endpoint page.
Credit Costs¶
AI-powered endpoints consume credits from your account balance:
| Operation | Credits |
|---|---|
| AI Chat message | Varies by model and response length |
| AI Writer generation | Varies by model and response length |
| AI Image generation | Varies by model and resolution |
| Knowledge Base query | 2 |
| Platform Knowledge query | 2 |
| Search question discovery | 3 |
| Dry-run session message | Varies by model |
When credits are exhausted, AI endpoints return 419:
{
"success": false,
"error": {
"code": "NO_CREDITS",
"message": "Insufficient credit balance. Please upgrade your plan."
}
}
Check your current balance at GET /api/v1/app/usage-data.