Error Handling
All Orsa API errors follow a consistent format with machine-readable error codes and human-readable messages.
Error Response Format
{
"success": false,
"error": {
"code": "INVALID_DOMAIN",
"message": "The provided domain could not be resolved",
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}Every error response includes:
| Field | Description |
|---|---|
success | Always false for errors |
error.code | Machine-readable error code (use this for programmatic handling) |
error.message | Human-readable description |
error.request_id | Unique ID for debugging — include this in support requests |
Error Codes
Client Errors (4xx)
| Code | HTTP Status | Description |
|---|---|---|
INPUT_VALIDATION_ERROR | 400 | Invalid request parameters or body |
UNAUTHORIZED | 401 | Missing or invalid API key |
FORBIDDEN | 403 | Insufficient permissions (e.g., free plan using paid features) |
NOT_FOUND | 404 | Resource not found (brand, crawl job, etc.) |
USAGE_EXCEEDED | 402 | Insufficient credits for the request |
RATE_LIMITED | 429 | Rate limit exceeded |
Server Errors (5xx)
| Code | HTTP Status | Description |
|---|---|---|
INTERNAL_ERROR | 500 | Unexpected server error |
WEBSITE_ACCESS_ERROR | 502 | Failed to access or extract data from the target website |
Handling Errors
TypeScript
import { Orsa, OrsaError } from 'orsa';
const client = new Orsa({ apiKey: process.env.ORSA_API_KEY });
try {
const brand = await client.brand.retrieve({ domain: 'invalid..domain' });
} catch (error) {
if (error instanceof OrsaError) {
switch (error.code) {
case 'INPUT_VALIDATION_ERROR':
console.error('Bad input:', error.message);
break;
case 'NOT_FOUND':
console.error('Brand not found');
break;
case 'RATE_LIMITED':
const retryAfter = error.headers?.['retry-after'];
console.error(`Rate limited. Retry in ${retryAfter}s`);
break;
case 'USAGE_EXCEEDED':
console.error('Out of credits — upgrade your plan');
break;
default:
console.error(`API error [${error.code}]: ${error.message}`);
}
// Always available for debugging
console.error('Request ID:', error.requestId);
}
}Python
from orsa import Orsa, OrsaError
client = Orsa(api_key=os.environ["ORSA_API_KEY"])
try:
brand = client.brand.retrieve(domain="invalid..domain")
except OrsaError as e:
if e.code == "INPUT_VALIDATION_ERROR":
print(f"Bad input: {e.message}")
elif e.code == "NOT_FOUND":
print("Brand not found")
elif e.code == "RATE_LIMITED":
print(f"Rate limited. Retry in {e.retry_after}s")
elif e.code == "USAGE_EXCEEDED":
print("Out of credits")
else:
print(f"API error [{e.code}]: {e.message}")
print(f"Request ID: {e.request_id}")Retry Strategies
Which Errors to Retry
| Error Code | Retry? | Strategy |
|---|---|---|
INPUT_VALIDATION_ERROR | ❌ No | Fix the request parameters |
UNAUTHORIZED | ❌ No | Check your API key |
FORBIDDEN | ❌ No | Upgrade your plan |
NOT_FOUND | ❌ No | The resource doesn't exist |
USAGE_EXCEEDED | ❌ No | Add credits or upgrade |
RATE_LIMITED | ✅ Yes | Wait for Retry-After, then retry |
INTERNAL_ERROR | ✅ Yes | Exponential backoff (max 3 retries) |
WEBSITE_ACCESS_ERROR | ⚠️ Maybe | Site may be down — retry once after 30s |
Exponential Backoff
const delay = baseDelay * Math.pow(2, attempt) + Math.random() * 1000;Add jitter (random component) to prevent thundering herd when multiple clients retry simultaneously.
Debugging
- Check the
request_id— include it when contacting support. - Check rate limit headers — you might be hitting limits without realizing it.
- Validate inputs client-side before making API calls to avoid unnecessary 400 errors.
- Log error codes, not just messages — codes are stable; messages may change.