Error Codes
Every error response follows the same format. Parse it once, handle it everywhere.
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or expired API key",
"request_id": "req_a1b2c3d4"
}
}
The code field is a machine-readable string. The message field is human-readable. The request_id maps to the audit trail.
Status codes
| Status | Code | Description |
|---|
| 400 | BAD_REQUEST | Malformed JSON, missing required fields |
| 401 | UNAUTHORIZED | Invalid API key, expired key, revoked key |
| 403 | FORBIDDEN | Insufficient scope, plan limit reached |
| 404 | NOT_FOUND | Resource doesn’t exist |
| 429 | RATE_LIMITED | Too many requests. Check Retry-After header |
| 500 | INTERNAL_ERROR | Something went wrong on our end. Contact support |
| 502 | BAD_GATEWAY | Upstream AI provider returned an error |
| 503 | SERVICE_UNAVAILABLE | Gateway overloaded or coordinator unreachable |
Handling 429 Rate Limited
When you hit rate limits, the response includes headers telling you when to retry:
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1710360000
| Header | What it means |
|---|
Retry-After | Seconds to wait before retrying |
X-RateLimit-Limit | Max requests per window |
X-RateLimit-Remaining | Requests left in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Back off for the number of seconds in Retry-After. Don’t retry in a tight loop.
Handling errors in code
const response = await fetch("https://gateway.takumo.io/v1/shield", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ content: code })
});
if (!response.ok) {
const { error } = await response.json();
switch (error.code) {
case "UNAUTHORIZED":
// Key is bad. Check config.
break;
case "RATE_LIMITED":
// Wait and retry.
const retryAfter = response.headers.get("Retry-After");
await sleep(Number(retryAfter) * 1000);
break;
case "BAD_GATEWAY":
// AI provider issue, not Takumo. Check provider status.
break;
default:
console.error(`Takumo error: ${error.code} - ${error.message}`);
}
}
Always include the request_id when contacting support. It maps directly to the audit trail.