Chat completions and Responses
Updated
- What each model supports
- Give reasoning models room
- The cost of the request, and its cached input, on its usage
- Turning reasoning off
- What the gateway does with a field you send
- Output limits
- Three things a request can be refused for
- Prefix cache retention
- Calling a paused model
- Related
- Responses
| Connection | Value |
|---|---|
| API base | https://api.runinfra.ai/v1 |
| Workspace key | rp_... |
| Process variable | RUNINFRA_GATEWAY_KEY |
| Billing | The existing funding rules of your open-model account. |
Replace MODEL_ID with an available id from Open models; read current capabilities, limits and prices from GET /v1/models.
Send a standard chat completion from the SDK you already use, with a live model id from GET /v1/models.
What each model supports
Requesting a capability a model does not have returns 400 with error.code hosted_capability_not_supported and param naming the field.
A model that is temporarily paused is reported in the live list and on its Model Library page.
Give reasoning models room
Reasoning models can spend their output budget before they answer. Reasoning tokens count toward max_tokens before the answer, so set max_tokens to at least the figure in the dated model table below where the live output limit permits it. A smaller budget can be spent entirely on reasoning and return a completion whose content is empty.
Measured on this endpoint on 2026-08-14 by sending the prompt "Write a TypeScript retry helper." and recording how often a complete answer came back. The dated model-specific source reference identifies the models in these two probes; these are historical measurements, not current model limits.
| Source probe | Use at least | What smaller budgets did |
|---|---|---|
| First reasoning-model probe | 16,384 tokens | At 2,048 and 4,096 the answer was empty every time. At 8,192 it was empty half the time. At 16,384 the model still stopped on its own after roughly 7,500 output tokens; raising the ceiling further did not make it generate more. |
| Second reasoning-model probe | 4,096 tokens | At 2,048 the answer was empty in 2 of 5 runs and cut off mid-sentence in 4 of 5. |
A 2,048-token budget can be fully consumed by reasoning before an answer; 16,384 leaves more room where the live limit permits it. The probes above billed output under the historical contract. Under the current usage contract, a response with no answer settles at zero.
For another model, read the recommended output budget on its page in the Model Library.
The budget is a ceiling, not a spend.
How to detect an exhausted budget in code
The open-models service preserves finish_reason: "length" and adds choices[].runinfra.output_status when a choice reaches its generation limit before producing final answer content.
Branch on choices[].runinfra.output_status.code. generation_limit_reached_before_answer means the choice ended for length. If another terminal finish_reason produced no answer content, the code is no_answer_content and you should inspect finish_reason before retrying.
The aggregate usage annotation appears only when every choice lacks a final answer. The open-models service does not estimate a reasoning-token count: completion_tokens stays the model-reported output total, and non_answer_completion_tokens records that those tokens produced no answer. The annotation changes no pricing; a response with no billable output settles at zero, as described below.
The cost of the request, and its cached input, on its usage
Every hosted model response carries the calculated charge for the request. On credits, it uses the settlement formula over the tokens the provider reported. On a coding plan or Standby, the calculated charge is zero and the usage value is reported separately. A response with no billable output settles at zero and prints 0. Beside the cost, every hosted response carries the count of input tokens that were billed at the cached input rate.
| Field | Type | Value |
|---|---|---|
usage.cost | number | US dollars, to eight decimals. One microcent is exactly 0.00000001 dollars, so nothing is rounded away. |
usage.runinfra.cost_microcents | integer | Calculated charge in microcents, the canonical figure: one cent is 1,000,000 microcents. Zero when the plan or Standby paid. |
usage.runinfra.plan_value_microcents | integer | Usage value at the applicable per-token rates. Present only when the plan or Standby paid. |
usage.runinfra.paid_from | string | plan or standby, present only with plan-funded or Standby-funded usage. Credit-funded usage retains the ordinary cost fields. |
usage.prompt_tokens_details.cached_tokens | integer | Input tokens billed at the cached input rate, in the field chat API clients already read. Present on every hosted response; 0 when no input was billed as cached. |
usage.runinfra.cached_input_tokens | integer | The same count, beside the cost, for a reader that only looks at the runinfra object. |
The cost covers the tokens the provider reported, at the model's prices for uncached input, cached input (when the model discloses a cache hit) and output. During a promotional free window it is 0, present, not absent.
The cached count is the number the cost was computed with. On a billable response, prompt_tokens - cached_tokens is exactly what was billed at the uncached input rate. The cached count is 0 rather than absent whenever nothing was billed as cached: a first send, a prompt too short to match, a response that settled at zero, or a model whose cache is shared across tenants. On a shared cache a hit is neither disclosed nor priced, so 0 there means no cached discount was applied, not that the cache did nothing. /v1/models says which models publish an isolated cache.
Your balance is debited in whole cents, and the fraction of a cent left over carries to your next request, so one debit can differ from one request's cost by less than a cent. Over any period the two agree to within one cent of carry, unless the balance has reached its debt floor, which can limit a debit.
On a stream, the cost and the cached count ride on the usage frame that is sent together with [DONE], so send stream_options: {"include_usage": true} to receive them (the one usage frame forwarded without it, when a stream produced no answer, carries them too). Content chunks carry neither. A stream that ends in an error never quotes a cost. A /v1/responses usage carries the cost in the same two fields, and the cached count as input_tokens_details.cached_tokens and runinfra.cached_input_tokens. The usage of an idempotent replay of a streamed request (X-RunInfra-Idempotent-Replay: true) carries the figures that were settled.
Read the funding headers for the request's admission decision. Plan usage reports current plan limits and policy; Credits and budget reports the credit balance.
If a connection ends before terminal usage arrives, do not infer a final charge from HTTP 200 or from a credit reservation. Recovery uses persisted usage evidence; missing or stale evidence cannot reconstruct every delivered token. Keep the request id and original Idempotency-Key to read the settled receipt; the same key never replays generated text. Getting a new answer requires a new key and starts new paid inference. Follow the retry procedure before retrying.
Turning reasoning off
On a model that supports disabling reasoning, reasoning_effort: "none" turns it off for one request. The completion then carries the answer and no reasoning stream. Because reasoning bills at the output rate, this is the largest per-request cost lever on short tasks.
Measured on this endpoint on 2026-08-16 with an identical prompt at temperature 0, turning reasoning off reduced the completion from 35 tokens to 3, from 335 to 5, and from 67 to 5 on three models. A one-line probe on another model on 2026-08-29 fell from 30 tokens at the default effort to 2 with none. These dated observations, whose models are identified in the source measurements, are not promised savings.
Some models cannot turn reasoning off. On Chat Completions they refuse reasoning_effort: "none" with 400, code hosted_parameter_not_supported and param: "reasoning_effort". The message names the smallest reasoning budget the model accepts: send that value.
On /v1/responses, a model that cannot turn reasoning off does not refuse reasoning: {"effort": "none"}: the request runs at the smallest budget the model supports, and the answer carries the header X-RunInfra-Hint-Warnings with reasoning_effort_clamped. The model still reasons, and those tokens count toward the output budget and bill at the output rate. To ask for that budget directly, send it. A model that cannot turn reasoning off and publishes no reasoning tiers at all (an empty reasoning_efforts) is not lowered: it refuses the request with the same 400 as on Chat Completions.
When you send no reasoning_effort, a model uses its default_reasoning_effort from the model list (null means none is declared, not that reasoning is off). One exception: for some models a request that sends no effort, can call tools (a non-empty tools and a tool_choice that is not "none") and has no JSON response_format runs at a lower effort than that default. Tool calls in the history alone do not trigger it. Whenever the gateway chooses the effort for a request that did not name one, the response header X-RunInfra-Reasoning-Effort-Applied carries the value it applied.
Use the live reasoning_efforts for measured, distinct tiers and default_reasoning_effort for the declared default. Some reasoning models publish no measured tiers; an empty list does not turn reasoning off. The model-specific source reference records the dated tier measurements and accepted synonyms. On some models reasoning_effort: "minimal" is refused with 400 and param: "reasoning_effort"; send the smallest accepted effort named in the refusal. On the source model with that restriction, reasoning: {"effort": "minimal"} maps to low.
The source's effort measurements used repeated runs per level at temperature 0 on 2026-08-16, with later observations dated individually. The following are model-specific observations, not universal reasoning tiers; the source reference identifies each configuration, and the live fields above carry its current measured tiers and declared default.
- One configuration used
maxby default and a reproducibly distinctmediumfor a request with tools and no JSON format when effort was omitted. The exact automatic-effort condition and applied-value header are described above. - Another answered without reasoning at the default, leaving nothing to turn off; its effort levels were not measured.
- A configuration with distinct
low,mediumandxhighproduced 113, 140 and 89 completion tokens on the probe, withxhighequal to the omitted default.highwas sent asxhigh. It refusednone; send the smallest budget named by the refusal. - Another measurably changed its reasoning at
mediumagainst a twice-identical baseline.highandmaxwere sent asxhigh, its maximum and default. - On a configuration whose effort deltas stayed inside its own run-to-run variance, treat effort as the off switch only. Another reasoned at the default and accepted
noneto turn it off, without measured levels between them. - In the 2026-08-27 measurement,
lowandhighwere honored as sent. Omitting the field, or sending another accepted value includingmedium, ran at the model's maximum effort.nonewas refused;lowwas its smallest budget. - In the endpoint measurement on 2026-10-19,
low,highandmaxwere distinct tiers, and the omitted field usedmax.minimal,mediumandxhighwere accepted synonyms formax. Chat Completions refusednone. - In the 2026-08-30 measurement,
nonereturned the answer directly;highandmaxwere sent asxhigh, its maximum and default;lowandmediumwere accepted as sent. Its refusal of the top-levelminimalspelling, and the reasoning-object alternative, are described above.
For a reasoning object, the source's compatibility mapping compares the requested sequence none / disabled, minimal, low, medium, high, xhigh, max. The model-specific source reference shows how each model maps an explicit effort level. For a model that cannot turn reasoning off, the current Responses behavior and warning header above apply; a model with no published tiers refuses the off request rather than being lowered. Do not infer these exceptions from an empty tier list.
How the effort levels behave between none and the ceiling
The vendor spellings enable_thinking, chat_template_kwargs.enable_thinking, thinking_budget and thinking are accepted for request-shape compatibility and have no effect on any listed model: a request relying on them runs at the model's default effort and can still produce billable reasoning. Send reasoning_effort or the reasoning object instead; both are honored.
The reasoning object
reasoning accepts effort, enabled, exclude: the unified reasoning object some routers send. include_reasoning is boolean. Conflicting reasoning and reasoning_effort return 400.
exclude: true or include_reasoning: false hides reasoning; reasoning tokens still bill. With hidden reasoning, logprobs cover the answer only. Stream chunks that carry reasoning have logprobs: null; a non-streaming answer keeps only the answer's entries, or null when they cannot be separated.
Only none or enabled: false requests reasoning off; other efforts keep reasoning on at the nearest reasoning tier at or below your request, or the model's lowest reasoning tier if none is lower. default keeps the model's own reasoning on.
What the gateway does with a field you send
Each top-level request field meets one of three fates: validated by the gateway and forwarded, forwarded untouched from the allowlist, or removed before forwarding because it is not on the allowlist.
- What happens to a field you send
- POST /v1/chat/completions
- Checked, then sent
- The gateway validates the type
- or the range first. A bad value
- is refused with 400 before the
- request is served or billed.
- model, messages, max_tokens
- Sent untouched
- On the forwarding allowlist.
- Any JSON value, no gateway
- check. The model can still
- reject it, as a 400.
- seed, user, service_tier
- Removed
- Not on the allowlist, so it is
- dropped before forwarding. The
- model never sees it and you
- get no warning.
- guided_json, length_penalty
- The allowlist is exhaustive. An unknown top-level field passes request validation and is still removed
- before the request reaches the model. Check the list before depending on a field.
Checked, then sent:
| Field | Type | Default | Behavior |
|---|---|---|---|
model | string | none | Required. Must resolve to a model your key can reach. |
messages | object array | none | Required, at least one message. Extra message fields pass through. Some models need system messages in a set order, see System message placement. |
stream | boolean | false | true returns an SSE response. |
temperature | number, 0 to 2 | none | Range checked, then forwarded. |
top_p | number, 0 to 1 | none | Range checked, then forwarded. |
top_k | integer | none | -1 or 0 turns it off. |
min_p | number, 0 to 1 | none | See logit_bias restrictions below. |
repetition_penalty | number, greater than 0 | none | Forwarded. |
max_tokens | integer, 1 or more | see Output limits | Per-generation output limit, counts toward the total output budget. |
max_completion_tokens | integer, 1 or more | see Output limits | Same. If you send both, the larger is used for the budget check. |
n | integer, 1 to 4 | 1 | Generated choices. The total output budget covers all of them. |
best_of | integer, 1 to 4 | 1 | Forwarded when present, included in the choice budget. |
stop | string or string array | none | At most 16 sequences, limited to 4 on some models; each sequence is at most 256 characters. The model-specific source reference identifies the models with the lower count. Exceeding either bound returns 400 hosted_parameter_not_supported. Remove excess sequences or shorten them, then retry. |
A message's role is system, developer, user, assistant or tool. Any other role returns 400. content may be a string, an array, or null. An assistant message needs content or a non-empty tool_calls. Each tool_calls[].function.arguments must hold a JSON object, such as "{}" for a call with no arguments. A tool message needs content and a non-empty string tool_call_id. Every other role needs content.
System message placement
A developer message is sent to the model as a system message. Most models accept system messages anywhere in the conversation and receive them where you put them.
Some models accept one system message, only as the first message. The model-specific source reference identifies them. For these models, the gateway repairs message placement before forwarding:
- Consecutive system messages at the start are merged into one, in order, separated by a blank line.
- A system message later in the conversation is sent as a
usermessage in the same position.
Every other message, tool call, and tool result keeps its position and content. A repaired conversation still extends the previous turn's prompt, so prompt caching keeps working across turns. Coding agents that send more than one system message, such as Claude Code and Codex CLI, run on these models without extra configuration. A leading system message that contains anything other than text is not merged, and the request returns 400 with param: "messages".
Sent untouched: the forwarding allowlist
The gateway accepts any JSON value on these, applies no default, and forwards it unchanged. The model can still reject the value, which comes back as 400 invalid_request_error.
audio, frequency_penalty, function_call, functions, logit_bias, logprobs, metadata, modalities, moderation, parallel_tool_calls, prediction, presence_penalty, safety_identifier, seed, service_tier, store, tool_choice, top_logprobs, user, verbosity, web_search_options.
Some allowlisted fields do carry gateway behavior:
| Field | Behavior |
|---|---|
functions, function_call | Forwarded unchanged, except on some models that refuse them with 400 hosted_parameter_not_supported unless a non-empty tools array is also sent. Send tools and tool_choice instead. The model-specific source reference identifies the exception. |
logit_bias, min_p | Some models refuse a non-empty logit_bias with 400, code hosted_parameter_not_supported and param: "logit_bias". An empty object is accepted. The same models refuse a min_p above 0 with the same code and param: "min_p", unless temperature is 0 (a request that sends no temperature is refused too). The message says to remove the field. |
logprobs, top_logprobs | Forwarded unchanged, except on some models that refuse a logprobs request with 400 hosted_parameter_not_supported. Remove the logprobs request or choose a model that accepts it; the model-specific source reference identifies the exception. |
reasoning_effort | Forwarded unchanged, subject to the model-specific refusals and rewrites in Turning reasoning off. When the gateway chooses an effort that the request did not name, X-RunInfra-Reasoning-Effort-Applied reports the value applied. |
response_format | Forwarded to a model that can enforce it; see Three things a request can be refused for. |
stream_options | The gateway always requests usage so it can settle. Client-visible usage normally needs include_usage: true; a stream with no final answer also forwards an annotated usage frame without opt-in. |
tool_choice | Some models refuse tool_choice: "required" with 400, code hosted_parameter_not_supported and param: "tool_choice". The message says what to send instead. Send "auto" or name one function where the model supports that choice. |
tools | An empty array is refused with 400 hosted_parameter_not_supported. Anything else is forwarded unchanged, with the two exceptions below. |
The two tools exceptions also apply to a json_schema response format:
- A bound pair in a tool's parameter schema that no value can satisfy (
minimumabovemaximumor an exclusive bound that closes the range,minItemsabovemaxItems,minLengthabovemaxLength,minPropertiesabovemaxProperties) is removed before the request is forwarded, because a forced tool call enforces the schema during generation and a range no value can satisfy cannot be enforced; satisfiable bounds are kept. - An
enumwith no values or apatternthat is not a valid regular expression cannot be repaired and is refused with400 hosted_parameter_not_supportednaming the path.
Forwarding does not guarantee the model accepts a particular tool definition.
Model-specific tool and JSON rules
The model-specific source reference describes a model on which tool_choice: "auto" and "none", parallel calls with distinct ids, and tool-result follow-ups are supported. Escaped and nested JSON arguments were verified through the endpoint on 2026-10-19.
For that model, with tool_choice: "required" or a named function choice, set function.strict: true on every tool. With non-strict tools, the endpoint may answer without a tool call even when you require or name one.
On that model, response_format with json_object and strict json_schema are supported. Enum values, maxItems and nested additionalProperties: false were verified. The schema applies after reasoning, so max_tokens must cover reasoning and the final JSON object. System messages may appear anywhere in the conversation. Stop strings can also match the reasoning stream and leave answer content empty, so inspect finish_reason and content before using a result.
Keep tools in every request whose history carries tool calls, and prefer tool_choice: "auto" over "none". Nothing refuses a request that leaves them out, but on some models the answer can then come back empty, with choices[].runinfra.output_status.code set to no_answer_content.
Removed before the request leaves the gateway
The allowlist is exhaustive: every other top-level field is dropped. Common examples are max_output_tokens, text, include, truncation and previous_response_id (Responses API fields on the Chat route); length_penalty; any nonstandard guided-decoding or beam-search field such as guided_json, guided_regex, guided_choice, guided_grammar and use_beam_search (use response_format and tools for constrained output); and num_return_sequences, num_beams and beam_width, which the gateway reads for the four-choice safety check and then removes.
Output limits
| Limit | Value |
|---|---|
| Total output tokens per request | The model's remaining context, that is its context window minus your prompt, across all generated choices. Oversized values are clamped, never refused. |
| Generated choices per request | 4 |
| Full response | 740 seconds, then 504 hosted_inference_response_timeout |
| First token on a stream | 180 seconds, then 504 hosted_inference_response_timeout |
| JSON request body | 32MB, then 413 payload_too_large. Bodies over 4MB need a Content-Length header, which every SDK sends |
| Images per request | 8 across all messages on vision models, except for the model in the model-specific image reference that permits 100. Exceeding the applicable count returns 400 hosted_parameter_not_supported. Remove older images or split them across requests. |
The body-size and image-count limits count every message in the request, including the images and history a client resends each turn. Base64 adds about a third to every inline image, so a 32MB body carries about 24MB of image files in total; drop older images from the conversation, split them across requests, or use a public image URL on a vision model. Remote image downloads have separate limits.
A text-only request that sets neither max_tokens nor max_completion_tokens defaults to 131,072 output tokens when context permits, or the model's own lower default for JSON output. Set an explicit budget to control the request.
Set an explicit max_tokens budget using the selected model's live max_output_tokens and remaining context. A generation that reaches its output budget reports finish_reason: "length", including when reasoning consumes the budget before an answer. Oversized max_tokens is clamped to the remaining context, with X-RunInfra-Output-Clamped: true and X-RunInfra-Output-Token-Ceiling reporting the value used.
Two bounds apply at once and the smaller one wins. Tokens are bounded by the remaining context above; wall-clock is bounded by the 740 second response limit, which at typical decode rates is the binding limit for very long generations. /v1/models publishes both, max_output_tokens beside response_time_ceiling_seconds, which carries the same 740 second figure, so size a long generation against the pair rather than the token figure alone.
The 504 message names the budget that was exceeded. For a non-streaming request, the error message suggests two remedies: stream: true (response headers arrive with the first token) and a smaller max_tokens. A first-token timeout on a stream suggests a smaller prompt, or retrying when the model has more capacity.
A model's context window is a separate per-model limit, published on that model's page in the Model Library. Served context is a property of how the model is served, not of the weights, so read it on the model page rather than a model card.
Three things a request can be refused for
Each one is refused up front with a 400 naming the field, rather than answered with output you cannot use.
A JSON format the model cannot hold while reasoning
response_format of json_schema or json_object is enforced during generation, and on some models that enforcement cannot start until the model has finished reasoning. The gateway reports an automatically applied value in the X-RunInfra-Reasoning-Effort-Applied response header. Send an incompatible effort beside the format and the request is refused with 400, code hosted_parameter_not_supported and param: "response_format".
On the models with this restriction, omitting reasoning_effort applies an effort measured to return a conforming object: none on some, or low on another where an explicit high also conforms. The model-specific source reference identifies which case applies; the response header tells you the automatically applied value.
The fix is the effort value sent alongside response_format:
Check each model's JSON mode requirements in the Model Library before changing the model in this request. response_format: {"type": "text"} constrains nothing and is never refused. On /v1/responses the same remedy is a top-level reasoning_effort beside text.format. Working snippets are in Structured output.
An image, audio, or video content part
Accepted input is declared per model in Open models. On a model with image-input support, image_url and input_image content parts accept a public http:// or https:// image URL, or an inline base64 data URL. Most vision models accept up to 8 images per request; the model in the model-specific image reference permits up to 100. The count covers every image in the conversation you send. The images in one request may decode to about 680 MiB of pixels in total, about 99 images at 1920x1080. Remote images must be PNG, JPEG, WebP or a non-animated GIF. Existing inline GIF input is unchanged.
The open-models service's gateway downloads remote images and sends their bytes inline to the model. A URL must be reachable on the public internet without cookies or authorization headers. The default ports are 80 for HTTP and 443 for HTTPS; an explicit URL may use only ports 80 or 443. Private and local addresses, embedded URL credentials, more than 3 redirects, and redirects from HTTPS to HTTP are refused. Signed URLs work if they meet these rules.
Each remote image is limited to 20 MiB of transferred bytes and 20 MiB after decompression, with at most 16,777,216 pixels. The gateway streams decompression for gzip, deflate and Brotli responses. These are the open-models service's gateway memory bounds; 1 MiB is 1,048,576 bytes.
A request containing remote images may carry at most 50 MiB of expanded image data URLs, including inline images and repeated URLs. Each complete data URL counts once per image part, including its base64 bytes and prefix. Identical URLs are downloaded once per request. The gateway-created JSON body is limited to 54 MiB. An inline-only request keeps its existing JSON request body limit.
Each image has a 10 second download limit, including DNS, redirects and its body, with 20 seconds for all images together, or less if the request's deadline comes sooner. Waiting for download capacity counts toward that time. Each gateway process permits 32 active image downloads and 256 queued downloads. A download waits at most 5 seconds for a slot or byte capacity, within its remaining deadline. A capacity refusal returns retryable 429 hosted_admission_congested with Retry-After; it does not mean the image URL is invalid.
A process-wide 256 MiB reservation budget covers cumulative wire and decoded bytes while downloads are in progress. The gateway reserves announced Content-Length bytes up front and adds reservations as decoded or unknown-length bytes arrive. Reservations are released when a download ends. This is not a limit on total process memory or completed inline request bodies; the existing per-request image and body limits still apply.
A refused remote image returns 400 with type: "invalid_request_error", code: "invalid_image_url" and param: "messages". The message names the part, such as messages[2].content[1], and the reason. It carries no Retry-After and sets x-should-retry: false. Use a reachable public URL within these limits, or send an inline data URL such as {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}. Retrying the same invalid image is not advised.
Image tokens are counted by the server inside prompt_tokens and bill at the model's input rate. The gateway's container checks catch known truncation and reject remote animated GIFs. These checks do not prove that every pixel can be decoded. A residual pixel decoder failure keeps the existing upstream 400 refusal instead of being reported as model unavailability.
On a model without declared image-input support, and for audio and video everywhere, a non-text content part inside messages returns 400 with param: "messages" and code: "hosted_parameter_not_supported", naming the model and the part to remove. The part is refused rather than stripped, so a request carrying media is never billed as if it were text.
A prompt-cache control field
prompt_cache_options and prompt_cache_retention are rejected with 400 hosted_parameter_not_supported. prompt_cache_key is accepted: when no session header supplies an identity, the gateway reads it as a cache-affinity hint and removes it before the request reaches the model. It grants no control over retention. Prefix caching, where a model has it, is automatic server behavior and is not controllable per request.
Prefix cache retention
Retention is what GET /v1/models states as cache_tiers and cache_retention. A model reporting cache_retention: "tiered_host_memory" can reuse an idle session's cached prefix beyond immediate memory pressure when the session resumes. Every other chat model reports "best_effort": cached prefixes are retained opportunistically and may be evicted at any time, with no retention promise. Both policies remain best effort. See Session affinity to keep follow-up turns close to a warm cache, and read usage.prompt_tokens_details.cached_tokens for each request's cached input.
Calling a paused model
A hosted model can be temporarily paused. The request returns 503 with code hosted_model_paused, charges nothing, and carries a Retry-After header with the seconds until availability is next rechecked, capped at 3,600, or 60 when no recheck time is known or the known one has already passed. See the 503 codes.
Related
Responses
The open models API serves POST /v1/chat/completions, POST /v1/responses, POST /v1/messages, POST /v1/messages/count_tokens, GET /v1/models, GET /v1/models/{model}, GET /v1/credits, and GET /v1/usage. Chat completions and Responses are chat-compatible. Messages and token counting use the Messages-compatible contract.
Not supported anywhere: /v1/completions (legacy, non-chat), /v1/files, /v1/assistants, /v1/threads, and /v1/batches. /v1/responses is a chat-completions compatibility adapter and does not implement state, hosted tools (web search, file search), conversation storage, or background jobs.
On a model whose page lists tool calling, function tools work end to end on /v1/responses:
- Tools, in both spellings: declare tools flat (
{"type": "function", "name", "parameters"}, the Responses shape) or nested underfunction(the chat shape). - Calls and results: the model's calls come back as
function_calloutput items withcall_id,name, andarguments; send results back asfunction_call_outputinput items with the matchingcall_id(string or content-part array), and resend prior output items ininputsince the endpoint is stateless. - Streams carry the full item lifecycle (
response.createdandresponse.in_progress,response.output_item.added,response.content_part.added, text andfunction_call_argumentsdeltas, the matchingdoneevents, andresponse.completed, which ends every stream), so Codex-style clients that build items fromoutput_item.donework unchanged.stream_options: {"include_obfuscation": true}adds random padding to each delta. - Harness fields:
storeis accepted and always treated as false, nothing is retrievable later.metadata,include(empty or any documented value; onlyreasoning.encrypted_contentcan add output here),truncation("auto"or"disabled"),parallel_tool_calls,service_tier,client_metadata,safety_identifier,stream_options,max_tool_calls, andprompt_cache_retentionare accepted; the hints among them have no effect. Input is never truncated: a prompt longer than the context window returnscontext_length_exceeded.previous_response_id,conversation, andpromptare accepted only asnull, andbackgroundonly asfalseornull; other values are refused with the reason.
On /v1/responses, output_text carries the final answer only. The model's thinking arrives as its own reasoning output item ahead of the message (raw text under content as reasoning_text parts, summary empty), and on streams as response.reasoning_text.delta and response.reasoning_text.done events inside a response.content_part.added and response.content_part.done pair. A completion that spends its whole token budget thinking returns an empty output_text with the thinking preserved in the reasoning item, so raise max_output_tokens if you see that.
A reply that max_output_tokens or a content filter cut short is not whole, and the response says so: status is "incomplete" and incomplete_details.reason is max_output_tokens or content_filter. A non-streaming body carries it directly. A stream still ends on response.completed, never on response.incomplete, and the response inside that event carries the same status and reason. Check status before you treat an answer as complete. Item status is advisory: only the item being written when the reply stopped is incomplete, so parse a function call's arguments before you run it.