Skip to main content

Errors

Every failure is a JSON body with an HTTP status:

{
"error": {
"message": "Budget has been exceeded! Current cost: 20.02, Max budget: 20.0",
"type": "insufficient_credits",
"param": null,
"code": "insufficient_credits"
}
}

Branch on code. It is a fixed vocabulary that we keep stable; message is for people and its wording may change. type groups codes into families that are handled the same way. The HTTP status tells you the same thing at a coarser grain, so existing clients that branch on it keep working.

Codes​

HTTPtypecodeWhat happenedWhat to do
400invalid_request_errorinvalid_requestThe request was malformed: a missing field, a value the model does not accept, a bad image.Fix the request. Do not retry as-is.
400not_found_errormodel_not_foundThe model name does not exist.Pick a name from GET /v1/models.
400insufficient_creditsinsufficient_creditsYour available credits are exhausted. Nothing ran and nothing was charged.Add credits, then retry.
400insufficient_creditskey_budget_exceededThis key's own credit limit is used up (your account may still have credits).Raise the key's limit, wait for its reset, or use another key.
400content_policy_violationcontent_filteredA safety check refused the request before the model ran. Not charged.Change the input. Retrying the same input gives the same answer.
401authentication_errorinvalid_api_keyNo key, an unknown key, or a deleted key.Check the Authorization: Bearer sk-... header.
401authentication_errorkey_expiredThe key's expiration date has passed.Create a new key.
403permission_errormodel_not_allowedThe key is valid but this model is outside your account's allowed set.Use a model from GET /v1/models.
403permission_errorforbiddenThe key may not perform this operation.Use a key with the right scope.
404not_found_errornot_foundUnknown path, or a video / job id that does not exist.Check the URL and the id.
409idempotency_erroridempotency_key_reusedThe Idempotency-Key was already used with a different body.Use a fresh key for a new request. See Retries.
409idempotency_erroridempotency_key_in_progressThe first request with this key is still running.Wait for Retry-After seconds, then retry the same request.
429rate_limit_errorrate_limitedYou exceeded your key's requests- or tokens-per-minute limit.Back off until Retry-After; see Limits.
429rate_limit_errorprovider_overloadedEvery deployment of the model is at capacity right now.Retry with backoff, or switch models.
5xxapi_errorprovider_errorThe model's provider failed. Not charged.Retry with backoff; if it persists, switch models.
504api_errortimeoutThe request ran past the 240-second window.Stream the response, shorten the request, or use an asynchronous endpoint.

Anything not in this table is an api_error. New codes may be added; existing ones are not renamed or removed.

The request ID​

Every response, success or failure, carries the header x-oceantoken-request-id. It is the Request ID shown in the console under Logs, where you can see the same request with its status, latency and cost. Log it with your errors and quote it when you write to us.

Retries​

Retry only on 429, 5xx and network failures (a dropped connection, a client-side timeout). Everything in the 400 range will fail the same way again until the request or the account changes.

Use exponential backoff with jitter, starting around one second and capping at about thirty; when the response carries Retry-After (a number of seconds), wait at least that long.

import random, time
from openai import OpenAI, APIStatusError, APIConnectionError

client = OpenAI(base_url="https://api.oceantoken.ai/v1", api_key=API_KEY, max_retries=0)

def with_retries(call, attempts=5):
for attempt in range(attempts):
try:
return call()
except APIConnectionError:
pass # network: retry
except APIStatusError as e:
if e.status_code not in (429, 500, 502, 503, 504) or attempt == attempts - 1:
raise
retry_after = e.response.headers.get("retry-after")
if retry_after:
time.sleep(float(retry_after)); continue
time.sleep(min(30, 2 ** attempt) + random.random())

Retrying a request that costs money​

Image, video and speech requests are charged when they run. A retry after a network timeout would otherwise run them a second time. Send an Idempotency-Key header (any unique string, up to 255 characters, for example a UUID) on POST /v1/images/generations, /v1/images/edits, /v1/videos and /v1/audio/speech:

  • The first request runs normally and its response is kept for 24 hours.
  • A retry with the same key and the same body gets that response back, marked with the header Idempotency-Replayed: true. Nothing runs and nothing is charged.
  • The same key with a different body is refused with 409 idempotency_key_reused.
  • A retry while the first request is still running gets 409 idempotency_key_in_progress with Retry-After.
  • A 5xx answer is not kept, so a retry after a provider failure runs again.

Keys are scoped to your API key. The header is ignored on every other endpoint, and on the endpoints above a request without it behaves exactly as before. Responses above 8 MB are not kept; the request still runs, only the replay does not apply.

Errors while streaming​

With "stream": true, an error that occurs before the first token is an ordinary JSON error response with the status above. An error after the stream has started arrives as a final data: chunk whose payload is the same error object, and then the stream ends. You are charged only for the tokens that were delivered.

Balance exhausted​

When available credits reach zero, requests are refused with 400 insufficient_credits. Nothing is run and nothing is charged. Top up and retry.

Content policy​

Requests that trip a safety check are refused with 400 content_filtered. The check runs before the model is called, so the request is not charged.

Finding a failed request​

Every failure is recorded in the console under Logs with its status and the response body. Search by the Request ID (x-oceantoken-request-id).