Responses API
POST https://api.uberllm.dev/v1/responses serves OpenAI's Responses API on every
chat model in the catalog. Use it from the OpenAI SDK (client.responses.create),
Codex CLI, or any app with a "Responses" mode. Requests go through the
same routing, retries, key limits and billing as /v1/chat/completions, and each
call is listed in Dashboard → Activity with its cost.
curl https://api.uberllm.dev/v1/responses \
-H "Authorization: Bearer $UBERLLM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek/deepseek-v4.1-flash",
"instructions": "Answer in one short sentence.",
"input": "What is the capital of France?"
}'
from openai import OpenAI
client = OpenAI(base_url="https://api.uberllm.dev/v1", api_key="ull_...")
r = client.responses.create(model="deepseek/deepseek-v4.1-flash", input="Say hi")
print(r.output_text)
No stored state
UberLLM does not keep responses for you to continue from later. Every reply has
"store": false, and you send the whole conversation in input on each call: earlier user messages, the model's
output items, and your tool results. Codex CLI already works this way.
These are refused with a 400 (code: unsupported_parameter) that names the field:
previous_response_idandconversationbackground: true- input items of type
item_reference - stored prompt templates (
prompt)
GET /v1/responses/{id} and the other stored-response routes do not exist.
Request
| Field | What happens |
|---|---|
model |
Any chat model id, alias or :floor / :nitro suffix, as on chat completions. |
input |
A string, or a list of items (below). |
instructions |
Sent to the model as the system message. |
stream |
true for server-sent events. |
max_output_tokens |
The output cap. |
temperature, top_p, seed, stop |
Passed to the model. |
tools, tool_choice, parallel_tool_calls |
See Tools below. |
reasoning.effort |
Passed on as the model's reasoning effort (models that think). |
text.format |
text, json_object, or json_schema (structured output). |
metadata |
Your own tags, shown on the Activity row, as on chat completions. |
provider, models |
UberLLM routing options, as on chat completions (Models & routing). |
store, include, prompt_cache_key, service_tier, truncation |
Accepted and not used. Encrypted reasoning is always returned. |
Input items
| Item | Becomes |
|---|---|
message with role user, assistant, system or developer |
A chat message. developer is sent as system. Content parts input_text, output_text, input_image (image_url: an https or data: URL) and input_file (file_data or file_url). |
function_call |
The assistant's tool call, kept in order. |
function_call_output |
The tool's result. Images in an output are sent in the next user turn. |
custom_tool_call, custom_tool_call_output |
A call to a custom (freeform) tool, and its result. |
reasoning |
The model's earlier reasoning, given back to the model on the next turn. |
web_search_call and other hosted-tool records |
Skipped. |
Item ids are accepted and ignored. file_id (uploaded files) is not supported in
input_image / input_file: send the file inline or by URL.
Tools
function:{"type": "function", "name", "description", "parameters", "strict"}. The model's call comes back as afunction_callitem withcall_id,nameandarguments(a JSON string). Answer with afunction_call_outputitem carrying the samecall_id.custom(freeform input, such as Codex'sapply_patch): the model is given the tool with one string input, and the grammar informat.definitionas its description. The call comes back as acustom_tool_callitem with the rawinput.namespace(Codex's MCP tools): each tool inside is offered to the model under its namespace and name joined (mcp__github__get_issue); the call comes back withnameandnamespacesplit again.- Hosted tools (
web_search,file_search,code_interpreter,image_generation,computer_use,mcp) run on OpenAI's own servers. They are left out of the request and the model answers without them.
tool_choice takes "auto", "none", "required", or
{"type": "function", "name": "…"}.
Response
{
"id": "resp_…",
"object": "response",
"created_at": 1793333237,
"status": "completed",
"model": "deepseek/deepseek-v4.1-flash",
"output": [
{"type": "reasoning", "id": "rs_…", "summary": [],
"content": [{"type": "reasoning_text", "text": "…"}], "encrypted_content": "ull1:…"},
{"type": "message", "id": "msg_…", "status": "completed", "role": "assistant",
"content": [{"type": "output_text", "text": "Paris.", "annotations": []}]},
{"type": "function_call", "id": "fc_…", "status": "completed",
"call_id": "call_…", "name": "get_weather", "arguments": "{\"city\":\"Paris\"}"}
],
"usage": {
"input_tokens": 43, "input_tokens_details": {"cached_tokens": 0},
"output_tokens": 23, "output_tokens_details": {"reasoning_tokens": 0},
"total_tokens": 66, "cost_usd": 0.0000184
},
"store": false
}
- A
reasoningitem appears when the model returns its reasoning. Send it back in the nextinputas it is:encrypted_contentcarries the reasoning so models that need it on the next turn get it. statusisincompletewhen the answer hitmax_output_tokens(incomplete_details.reason: "max_output_tokens") or a content filter ("content_filter").usage.cost_usdis what the call was billed. Non-streaming replies also carry it in theX-UberLLM-Cost-USDheader, and every reply hasX-Request-Id, the id of its Activity row.- The settings you sent (
instructions,tools,temperature,max_output_tokens…) are echoed back as OpenAI does.
Streaming
With "stream": true the reply is server-sent events, each with type and a
sequence_number starting at 0:
response.created,response.in_progress- For each output item:
response.output_item.added, then its deltas, thenresponse.output_item.donewith the finished item:- text:
response.content_part.added,response.output_text.delta…,response.output_text.done,response.content_part.done - reasoning:
response.reasoning_text.delta…,response.reasoning_text.done - function call:
response.function_call_arguments.delta…,response.function_call_arguments.done - custom tool call:
response.custom_tool_call_input.done
- text:
response.completed(orresponse.incomplete) with the whole response and itsusage, includingcost_usd.
While a slow model is starting, : ping comment lines keep the connection open.
Every SSE client skips them.
Errors
A request refused before the stream starts gets an HTTP status and OpenAI's error
envelope, the same as chat completions (401, 402 out of credit, 404 unknown
model, 429; see Errors). If something fails after the stream has
started, it ends with response.failed, whose response.error has code and
message. A stream that stops before the model finished also ends with
response.failed, never with response.completed, so clients retry instead of
keeping half an answer.