NewPoint any agent at UberLLM — one key, every model, list price or less.

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_id and conversation
  • background: 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 a function_call item with call_id, name and arguments (a JSON string). Answer with a function_call_output item carrying the same call_id.
  • custom (freeform input, such as Codex's apply_patch): the model is given the tool with one string input, and the grammar in format.definition as its description. The call comes back as a custom_tool_call item with the raw input.
  • 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 with name and namespace split 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 reasoning item appears when the model returns its reasoning. Send it back in the next input as it is: encrypted_content carries the reasoning so models that need it on the next turn get it.
  • status is incomplete when the answer hit max_output_tokens (incomplete_details.reason: "max_output_tokens") or a content filter ("content_filter").
  • usage.cost_usd is what the call was billed. Non-streaming replies also carry it in the X-UberLLM-Cost-USD header, and every reply has X-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:

  1. response.created, response.in_progress
  2. For each output item: response.output_item.added, then its deltas, then response.output_item.done with 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
  3. response.completed (or response.incomplete) with the whole response and its usage, including cost_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.