Docs

Errors and rate limits

Every error code the API returns, the rate-limit headers, and how to retry.

Error format

Errors use the format of the endpoint you called, so your SDK raises its usual exception types.

OpenAI endpoints (/v1/chat/completions, /v1/responses, and /v1/models without an anthropic-version header):

{
  "error": {
    "message": "Your organization has no remaining credit. Top up to continue.",
    "type": "insufficient_quota",
    "code": "insufficient_credits",
    "param": null
  }
}

Anthropic endpoints (/v1/messages, /v1/messages/count_tokens, and /v1/models with an anthropic-version header):

{
  "type": "error",
  "error": {
    "type": "billing_error",
    "message": "Your organization has no remaining credit. Top up to continue."
  }
}

Every error response also carries the header x-interodin-error-code with the code from the table below. Branch on the HTTP status and on error.code (OpenAI format) or on the x-interodin-error-code header (any format). In the Anthropic format several codes share one error.type (for example, all three 403 codes are permission_error), so the header is the reliable way to tell them apart. Don't match on message, because the wording can change.

Error codes

StatusCodeMeaningWhat to do
400invalid_requestThe body is not valid JSON, misses a required field, or a field has an invalid value, for example max_tokens of 0.Fix the request.
400unsupported_parameterA parameter is not supported, for example previous_response_id.Remove it. For Responses, send the full conversation in input.
400image_input_not_supportedThe model is text-only and you attached an image to the newest message. Images in tool results or earlier turns are replaced by a text note instead of failing.Remove the image, or use a model with vision.
400unsupported_contentThe request contains file or document input (file, input_file or document parts, or any part with a file, file_id, file_data or file_url key, even without a type) in the newest message. No model accepts it. Files in tool results or earlier turns are replaced by a text note.Send the text content instead.
400content_refusedThe model declined to process this request. Nothing is charged for the refused request.Rephrase the request.
400context_length_exceededInput plus max_tokens exceeds the context window.Shorten the input or lower max_tokens.
401invalid_api_keyThe key is missing, malformed or revoked.Check the header and the key.
402insufficient_creditsThe organization's balance is used up.Top up in the console, or turn on auto-recharge.
403model_not_allowedThe key is not allowed to use this model.Use an allowed model, or edit the key.
403key_spend_limit_reachedThe key reached its monthly spend limit.Raise the limit or wait for next month.
403organization_suspendedThe organization is suspended.Contact support.
404model_not_foundThe model id does not exist.Use an id from GET /v1/models, such as odin-v1.
404not_foundThe path does not exist, for example GET /v1/responses/{id} (responses are not stored).Check the URL.
413request_too_largeThe request body is larger than 10 MB.Send less data per request.
429rate_limit_exceededToo many requests or tokens per minute.Wait for Retry-After seconds, then retry.
429concurrency_limitYour organization has too many requests in flight at once.Wait for Retry-After (1 second), or send fewer requests in parallel.
500internal_errorSomething failed on our side.Retry with backoff. If it persists, contact support and include the x-request-id.
502upstream_unavailableThe model is temporarily unavailable, or it stopped sending data for 300 seconds.Retry with backoff.
504timeoutThe model took too long to respond.Retry. For long outputs, stream the response.

Every response, errors included, carries an x-request-id header. Include the id when you contact support. Because we never store request content, the id is how we find your request.

If your client disconnects before the reply is complete, the request appears in your request logs with status 499 (client_closed_request). You never receive this status yourself, because the connection is already closed. If you abort a request that is not streaming, you are billed for the input in full plus an estimate of the output produced so far.

Errors during a stream

Once a stream has started, the status code is already 200. If generation fails after that point, the stream ends with an error event in the same format as the endpoint:

  • Chat Completions: a final data: {"error": {...}} line.
  • Responses: an event: error with a {"type": "error", "code": "...", "message": "...", "param": null} payload.
  • Messages: an event: error with a {"type": "error", ...} payload.

A failure during a stream is reported as upstream_unavailable. Input tokens are billed in full. Output is billed for what was delivered before the failure, estimated from the delivered text when the model never reported usage.

Rate limits

Limits apply per API key and per organization, per minute, to both requests and tokens (input plus output). Responses from the generation endpoints (/v1/chat/completions, /v1/responses, /v1/messages, /v1/messages/count_tokens) report the current window. They report whichever of your key's or your organization's windows is closer to its limit. GET /v1/models doesn't send these headers, and neither do errors that happen before the rate-limit check, such as 401 or a 400 for an invalid request.

HeaderMeaning
x-ratelimit-limit-requestsRequests allowed per minute.
x-ratelimit-remaining-requestsRequests left in the current window.
x-ratelimit-reset-requestsTime until the current one-minute request window ends, in whole seconds with an s suffix, for example 12s.
x-ratelimit-limit-tokensTokens allowed per minute.
x-ratelimit-remaining-tokensTokens left in the current window.
x-ratelimit-reset-tokensTime until the current one-minute token window ends, for example 12s.

When you exceed a limit you get 429 rate_limit_exceeded with a Retry-After header, in seconds. Your organization can also have only a limited number of requests in flight at once; past that you get 429 concurrency_limit with Retry-After: 1. Token counting with /v1/messages/count_tokens is free and works at a zero balance, but it still counts toward the rate limits and the in-flight cap. If you need higher limits, contact support.

Retrying

Retry only these responses:

  • 429
  • 500
  • 502
  • 504
  • network errors

Use exponential backoff with jitter, and honor Retry-After when it is present. Never retry other 4xx errors unchanged, because they will fail again.

The OpenAI and Anthropic SDKs already retry these statuses. You can raise the retry count and the timeout:

import OpenAI from 'openai'

const client = new OpenAI({
  baseURL: 'https://api.interodin.com/v1',
  apiKey: process.env.INTERODIN_API_KEY,
  maxRetries: 4,
  timeout: 120_000,
})

const models = await client.models.list()
console.log(models.data.map((m) => m.id))

On this page