跳到主要内容

Errors

Every failure is a JSON body with an HTTP status. This is the answer to a request sent without a key:

{
"error": {
"message": "Missing API key. Send it as: Authorization: Bearer <your OceanToken key>.",
"type": "authentication_error",
"param": null,
"code": "invalid_api_key"
}
}

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. Managing API keys is one: an API key cannot create, change, rotate or delete keys.Manage keys in the console; otherwise 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 channel of the model is at capacity right now. See Retries and failover.Retry with backoff, or switch models.
5xxapi_errorprovider_errorThe model failed, after the gateway's own retry and failover. Not charged.Retry with backoff; if it persists, switch models.
408api_errortimeoutA stream received nothing from the model for 120 seconds after it started.Retry with backoff.
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​

Responses to model requests carry the header x-oceantoken-request-id. Log it with your errors and quote it when you write to us. A request refused for its key (401) never reached a model and has none.

A successful response also has an id in its body, and every chunk of a stream carries the same id. That is the Request ID the console shows under Logs, with the request's status, latency and cost, and the id that GET /v1/usage/{request_id} looks up.

Retries​

Before an error reaches you, the gateway has already retried it and, where the model has another channel, moved the request there. Retries and failover explains what it handles and what it leaves to you.

Retry only on 429, 5xx, a timeout error 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.

# Retry only what can succeed on a second try: 429, 5xx and network failures,
# with exponential backoff and jitter, honouring Retry-After when it is sent.
import os
import random
import time

from openai import APIConnectionError, APIStatusError, OpenAI

client = OpenAI(
base_url=os.getenv("OCEANTOKEN_BASE_URL", "https://api.oceantoken.ai/v1"),
api_key=os.environ["OCEANTOKEN_API_KEY"],
max_retries=0, # the loop below does the retrying
)


def with_retries(call, attempts=5):
for attempt in range(attempts):
try:
return call()
except APIConnectionError:
if attempt == attempts - 1:
raise
delay = None # network failure: back off and try again
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")
delay = float(retry_after) if retry_after else None
time.sleep(delay if delay is not None else min(30, 2**attempt) + random.random())


models = with_retries(lambda: client.models.list())
print(len(models.data), "models")

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.

A stream that ends early, because the connection broke, you disconnected, or an error cut it off, is billed at the model's usual prices for:

  • its prompt, in full;
  • the output streamed up to that point. A reasoning model's streamed reasoning counts as output, even when no answer text had arrived yet.

Output the model may have gone on generating after the stream stopped is not billed. A stream that ended before producing any output is not billed at all.

Balance exhausted​

When your balance is at or below $0, every request is refused with 400 insufficient_credits, free models included. Nothing is run and nothing is charged. Add credits 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. Find it by its time and model, and quote its x-oceantoken-request-id when you write to us.