Skip to content
Get an API key

Reference

Updated

Errors

The error envelope, every status and code the helping.io API returns, what each one asks you to do, and the timeouts and retry rules to design for.

Every error on /v1 is the standard Responses API envelope, including the ones raised before your request reaches the endpoint, such as a bad key.

{
"error": {
"message": "missing or malformed Authorization header",
"type": "invalid_request_error",
"param": null,
"code": "invalid_api_key"
}
}
  • message is human-readable and safe to log.
  • type follows from the HTTP status, so SDK retry logic can branch on it. The one exception is insufficient_quota: a 429 that carries type insufficient_quota, as OpenAI’s own quota refusal does, so a client that handles that refusal from OpenAI handles this one.
  • code is the specific condition, always lowercase, and is the field to switch on in your own code.
  • param names the request field a refusal is about, when it is about one: model, input, metadata, tool_choice, max_output_tokens and the rest are reported as you sent them, so your error handler can branch on the field instead of reading the message. It is null for everything else, including a body that will not parse, a bad key, and every rate limit. Read it when it is there; do not require it.
HTTP statustype
400invalid_request_error
401invalid_request_error
402invalid_request_error
403permission_error
404not_found_error
409invalid_request_error
429insufficient_quota
429rate_limit_error
500api_error
502api_error
503overloaded_error
504api_error

Any other 4xx carries invalid_request_error.

Authentication, authorization, and billing

Section titled “Authentication, authorization, and billing”
StatuscodeRetry-AfterMeaning
401invalid_api_keyNoThe Authorization header is missing, malformed, or names a key that is not usable; check the key.
403access_deniedNoThe key is valid but is not entitled to what the request asked for; ask your administrator.
429insufficient_quotaNoThe organization has no active plan, or no credit balance to pay for the request; the message says which.
402payment_method_requiredNoThe organization has no payment method on file, so no request can be served; add one in the billing settings.
503auth_unavailableYesYour credentials could not be checked this instant, which is not a verdict on the key; retry after the Retry-After header.

insufficient_quota means your organization cannot pay for the request: it has no active plan, or it has no credit balance. The message says which. It is not a rate limit, and retrying does not change the answer.

Nothing is created and nothing is charged when one of these comes back.

StatuscodeparamMeaning
400invalid_inputThe offending fieldThe request cannot be honored as written; the message names the field and the rule it broke.
400context_length_exceededAlways nullThe conversation plus this turn's input is larger than the model's context window; start a new conversation, send less, or set truncation to auto to let the oldest turns be dropped.
400model_policy_violationThe offending fieldYour organization's policy does not allow the model this request selected; the message names the model and the rule that refused it, and param is `model`.
404resource_not_foundAlways nullThe path or the previous response named by this request is not available to you.
StatuscodeMeaning
409idempotency_conflictThis Idempotency-Key was already used within the last 24 hours, and what it made cannot be returned for this request: the body differs, or this API key cannot read it. Nothing was created.
409idempotency_in_progressThe first request with this Idempotency-Key is still being processed. Nothing was created; send the request again shortly.

See Idempotency.

Entries marked Retry-After carry that header; honor it. See Rate-limit headers for the headers that let you avoid most of them.

StatuscodeRetry-AfterMeaning
429rate_limit_exceededYesThis request exceeded a rate limit that applies to you; retry after the Retry-After header.
429stream_limit_exceededYesYou are already holding the maximum number of open streams; nothing was started, so retry once one ends.
429scope_budget_exceededNoA spend budget set for your organization, a member, or an API key is exhausted; the message states which.
429insufficient_creditsNoThe organization's credit balance is exhausted; top up in the billing settings.
503gateway_overloadedYesThe API could not accept this request and nothing was created; retry after the Retry-After header, and retrying with the same Idempotency-Key still deduplicates.
503database_unavailableYesThe request could not be served this instant and nothing was created; retry after the Retry-After header.
StatuscodeMeaning
429provider_rate_limitedThe model provider rate limited this request; retry, honoring Retry-After when one is present.
502provider_overloadedThe model provider reported that it is overloaded; retry shortly.
502provider_unavailableThe model provider did not complete the request; retry shortly.

A provider that rate limits your request is refused with 429, carrying the envelope the table above describes:

Response
{
"error": {
"code": "provider_rate_limited",
"message": "The upstream model provider rate limited this request; retry later.",
"param": null,
"type": "rate_limit_error"
}
}

Non-transient provider failures do not use the error envelope. They arrive as a 200 whose response object has status: "failed" and an error with a code and a message. See Failed responses.

StatuscodeMeaning
504request_timeoutThis request's wait window ended before a result was available and no response could be read back for it; quote the x-request-id response header when reporting it.
500internal_server_errorThe request could not be completed; quote the x-request-id response header when reporting it.
500database_errorThe request could not be completed; quote the x-request-id response header when reporting it.
500item_unreadableThe item is stored but could not be read, so it cannot be returned. A list of items names it in helpingio:unreadable and returns the others.

item_unreadable answers a request for one item that is stored but could not be read. A list of items does not fail because of it: the list names the item in helpingio:unreadable and returns the others.

A response that fails after it has started is not an HTTP error. It is a response object with status: "failed" and a populated error:

{
"id": "resp_…",
"object": "response",
"status": "failed",
"output": [],
"error": {
"code": "provider_client_error",
"message": "The upstream model provider rejected the request."
},
"usage": {
"input_tokens": 100,
"input_tokens_details": { "cached_tokens": 0, "cache_write_tokens": 0 },
"output_tokens": 0,
"output_tokens_details": { "reasoning_tokens": 0 },
"total_tokens": 100
}
}

The same shape arrives inside a streaming response.failed event. usage, when present, shows what the failed response consumed; read it with a presence check.

error.code on this channel is a closed set, and a failure this surface does not name separately arrives as run_failed. Switch on the code; the message is written for a person to read and may be reworded.

codeMeaning
budget_exceededThe spending limit for a single response was reached. The message states what was spent against the limit, in credits.
canceledThe request was canceled before it finished.
end_user_blockedThe model provider refuses requests for the end user this request was sent for, named by its safety_identifier or user. Sending it again will not succeed for that end user.
insufficient_creditsThe organization ran out of credits while the request was running. Add credits and send it again. The message states the balance, in credits.
org_daily_budget_exceededThe organization's daily spending limit was reached. The message states what was spent against the limit, in credits.
provider_client_errorThe upstream model provider rejected the request. The request reached the provider and came back refused.
provider_errorThe upstream model provider returned an unexpected error.
provider_overloadedThe upstream model provider is overloaded. Retry later.
provider_rate_limitedThe upstream model provider rate limited the request. Retry later.
provider_unavailableThe upstream model provider failed to complete the request. Retry later.
run_failedThe request did not complete. Send it again, and contact support with the response id if it keeps failing.
scope_budget_exceededA spending limit set for this period was reached. The message states what was spent against the limit, in credits.

Five of these codes are in the HTTP tables above as well, because where a failure reaches you depends on how you asked:

  • A non-streaming POST /v1/responses answers an upstream provider failure with an HTTP error, so provider_rate_limited, provider_overloaded and provider_unavailable arrive there with a status.
  • A streaming request answers 200 before it can know, so the same failure arrives in the response object instead. So does a GET /v1/responses/{response_id}, which answers what happened rather than refusing.
  • scope_budget_exceeded is an HTTP refusal when the limit is already spent as the request arrives, and a failed response when it runs out while the request is running.
  • Out of credits as the request arrives is the HTTP refusal insufficient_quota. insufficient_credits is the later, rarer case: an HTTP refusal when the request was accepted but the balance could not pay to start it, and a failed response when credits run out while the request is running. Handle both codes the same way.

A spending failure in a failed response states your own figures in credits, to one decimal place: what was spent against the limit, or the balance. Switch on the code; the figures are for the person reading the message.

Once a stream is open the HTTP status has already been sent, so every later failure is in-band:

  • response.failed carries the failed response object above.
  • response.incomplete means the response stopped early, for example at max_output_tokens. Read incomplete_details.reason.
  • An error event carries a failure that belongs to neither: the stream itself could not deliver a result.

The codes an error event can name:

codeMeaning
response_unresolvedThe stream ended before a result was ready. The response may still finish: retrieve it by the id response.created carried. Retrying without the same Idempotency-Key starts a second response.

The rate limits, and the headers that report them, are on Rate limits.

Bound Value What happens
Non-streaming wait 90 seconds The in-progress response object is returned with 200. Poll by id
Stream ceiling 300 seconds The stream closes with its terminal event. Retrieve by id
Stream keep-alive every 15 seconds An SSE comment frame keeps intermediaries from timing out a quiet stream

POST /v1/responses honors the Idempotency-Key header, and so do the requests that create a conversation, update one and add items to one. The official SDKs do not send it on their own, so pass it yourself in the request options and reuse it when you retry:

  • A retried request with the same key and the same body returns the first request’s response, so the model produces output and bills once. That includes a request that sends function_call_output items.
  • The same key with a different body is a 409 with the code idempotency_conflict, and nothing is created.
  • A retry that arrives while the first request is still being processed is a 409 with the code idempotency_in_progress. Nothing is created; send it again shortly.
  • A request that is refused, for example with a 400, does not use up its key, so you can correct it and send it again with the same key.
  • A key is remembered for 24 hours. After that, the same key starts a new request.
  • Keys belong to your organization, and each endpoint keeps its own keys. A retry from a different API key than the first request’s is a 409 with the code idempotency_conflict when that key cannot read what the first request made. On a conversation’s own endpoints, the same key and body sent to a different conversation is a 409 with the code idempotency_conflict.
  • Keys are printable ASCII, spaces included, at most 255 bytes. A longer key, or one with any other character, is a 400. Empty keys are ignored.
  • Without a key, a retry after a delivery failure creates a second response and a second charge.
  • Honor Retry-After on every 429 and 503. Use exponential backoff with jitter when the header is absent.
  • Watch the remaining-requests header above and slow down before it reaches zero.
  • Send an Idempotency-Key on every create so retries are safe.
  • Prefer streaming for long work. A stream is bounded at 300 seconds, a non-streaming wait at 90.