Skip to main content
Every error response follows the same format. Parse it once, handle it everywhere.

Response format

{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded. Try again in 30 seconds.",
    "request_id": "req_a1b2c3d4"
  }
}
  • code — Machine-readable string. Use this in your error handling logic.
  • message — Human-readable explanation. Suitable for logging but not for display to end users.
  • request_id — Unique identifier that maps to the audit trail. Include it when contacting support.

Status codes

StatusCodeWhen
400BAD_REQUESTMalformed JSON, missing required fields, invalid parameters
401UNAUTHORIZEDMissing API key, invalid key, expired key, revoked key
403FORBIDDENKey lacks required scope, feature not available on your plan
404NOT_FOUNDEndpoint doesn’t exist, resource not found
429RATE_LIMITEDToo many requests. Respect the Retry-After header
500INTERNAL_ERRORServer error. These are our fault — contact support with the request_id
502BAD_GATEWAYThe upstream AI provider returned an error
503SERVICE_UNAVAILABLEGateway overloaded, coordinator unreachable, maintenance

Handling errors in code

const response = await fetch("https://gateway.takumo.io/v1/tokenize", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ content: sourceCode, filename: "config.ts" })
});

if (!response.ok) {
  const { error } = await response.json();

  switch (error.code) {
    case "UNAUTHORIZED":
      // Key is invalid or revoked. Check your configuration.
      break;
    case "RATE_LIMITED":
      const retryAfter = Number(response.headers.get("Retry-After"));
      await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
      // Retry the request
      break;
    case "BAD_GATEWAY":
      // AI provider issue, not Takumo. Check provider status page.
      break;
    default:
      console.error(`Takumo error: ${error.code} - ${error.message}`);
  }

  throw new Error(`${error.code}: ${error.message} (${error.request_id})`);
}

Common error scenarios

400 BAD_REQUEST

{
  "error": {
    "code": "BAD_REQUEST",
    "message": "Missing required field: content",
    "request_id": "req_x1y2z3"
  }
}
Check that your request body is valid JSON with all required fields.

401 UNAUTHORIZED

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or expired API key",
    "request_id": "req_x1y2z3"
  }
}
Verify your API key is correct and hasn’t been revoked. Keys use the tk_live_ or tk_test_ prefix.

429 RATE_LIMITED

HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1710360000
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded. Try again in 30 seconds.",
    "request_id": "req_x1y2z3"
  }
}
Wait for the number of seconds in Retry-After. Do not retry in a tight loop. See Rate Limits for per-plan limits.

502 BAD_GATEWAY

{
  "error": {
    "code": "BAD_GATEWAY",
    "message": "Upstream provider returned 500",
    "request_id": "req_x1y2z3"
  }
}
This means the AI provider (Anthropic, OpenAI, etc.) had an error. Check the provider’s status page. This is not a Takumo issue.
Always include the request_id when contacting support. It maps directly to the audit trail and helps us diagnose the issue quickly.