/v1/responses or /v1/chat/completions: Which API to Use
Two OpenAI-compatible endpoints with different request and response shapes. On Zukko, Chat Completions works with every model, Responses with Claude and GPT.
4 min · · API keys · development
Responses API or Chat Completions is a question that comes up whenever a client or library asks you to pick an OpenAI "wire protocol". Zukko supports both: /v1/chat/completions works with every model, and /v1/responses works with Claude and GPT models. Here is how their requests and responses differ and which one to choose.
The two APIs in brief
Chat Completions (POST /v1/chat/completions) is OpenAI's long-standing standard: you send a messages array with roles and get choices[0].message.content back. Almost every OpenAI-compatible client supports it. The README of OpenAI's official Python SDK calls it the previous standard and says it is supported indefinitely.
Responses (POST /v1/responses) is OpenAI's newer API. Input goes into the input field (a string or an array of messages), the system instruction into a separate instructions field, and the answer comes back as an array of output items. The Python SDK offers a convenient response.output_text property with the final text.
Differences at a glance
| Parameter | /v1/chat/completions | /v1/responses |
|---|---|---|
| Input | messages with role | input: a string or an array of messages |
| System instruction | a message with role: system | the instructions field |
| Answer length limit | max_tokens | max_output_tokens |
| Answer text | choices[0].message.content | output items; output_text in the SDK |
| Usage fields | prompt_tokens, completion_tokens | input_tokens, output_tokens |
| Streaming | chat.completion.chunk chunks | events such as response.output_text.delta |
| Models on Zukko | all: claude-*, gpt-*, glm-* | claude-* and gpt-*, no GLM |
| Used by | Cursor, Cline, Kilo Code, LangChain and most OpenAI-compatible clients | Codex |
Chat Completions: an example
curl https://api.zukko.pro/v1/chat/completions \
-H "Authorization: Bearer fetch_your_key" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-5",
"messages": [
{"role": "system", "content": "You are an experienced Python developer. Be brief."},
{"role": "user", "content": "How do I read a JSON file?"}
],
"max_tokens": 1000
}'Response (abridged):
{
"object": "chat.completion",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "Use the json module..."},
"finish_reason": "stop"
}
],
"usage": {"prompt_tokens": 38, "completion_tokens": 120, "total_tokens": 158}
}The same in Python:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.zukko.pro/v1",
api_key=os.environ["OPENAI_API_KEY"],
)
response = client.chat.completions.create(
model="glm-5.3-flash",
messages=[
{"role": "system", "content": "You are an experienced Python developer. Be brief."},
{"role": "user", "content": "How do I read a JSON file?"},
],
max_tokens=1000,
)
print(response.choices[0].message.content)Streaming: on Zukko the last chunk carries usage and an empty choices, so check before accessing chunk.choices[0]:
stream = client.chat.completions.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "Explain Python generators"}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)Responses: an example
curl https://api.zukko.pro/v1/responses \
-H "Authorization: Bearer fetch_your_key" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.6-luna",
"instructions": "You are an experienced Python developer. Be brief.",
"input": "How do I read a JSON file?",
"max_output_tokens": 1000
}'Response (abridged):
{
"object": "response",
"status": "completed",
"output": [
{
"type": "message",
"role": "assistant",
"content": [{"type": "output_text", "text": "Use the json module..."}]
}
],
"usage": {"input_tokens": 38, "output_tokens": 120, "total_tokens": 158}
}In Python with the official SDK (you need a version that has client.responses):
response = client.responses.create(
model="gpt-5.6-sol",
instructions="You are an experienced Python developer. Be brief.",
input="How do I read a JSON file?",
max_output_tokens=1000,
)
print(response.output_text)
print(response.usage.input_tokens, response.usage.output_tokens)A multi-turn conversation is passed as an array in input:
response = client.responses.create(
model="claude-sonnet-5",
input=[
{"role": "user", "content": "What is the capital of France?"},
{"role": "assistant", "content": "Paris."},
{"role": "user", "content": "Which river flows through it?"},
],
)
print(response.output_text)Streaming in Responses is a stream of typed events; text arrives in response.output_text.delta events:
stream = client.responses.create(
model="gpt-5.6-luna",
input="Explain Python generators",
stream=True,
)
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)On OpenAI's platform, Responses also has platform features: server-side conversation storage (store, previous_response_id) and built-in tools. Do not rely on them through the gateway: pass the history in input yourself, as above, and test any feature you need on your model.
Which models work with which endpoint
| Model | /v1/chat/completions | /v1/responses |
|---|---|---|
claude-opus-5-5 | yes | yes |
claude-sonnet-5 | yes | yes |
gpt-5.6-sol | yes | yes |
gpt-5.6-luna | yes | yes |
glm-5.3 | yes | no |
glm-5.3-flash | yes | no |
A request to a GLM model via /v1/responses fails with an error; use /v1/chat/completions for these models. The lineup changes: the current list is at GET https://api.zukko.pro/v1/models or on the "Models" page in the dashboard.
Codex: why only Responses
Codex is OpenAI's coding agent. In the Codex configuration reference, the provider setting wire_api accepts a single value, responses, which is also the default. So Codex talks to models only through the Responses API, and on Zukko it works with Claude and GPT models but not GLM. A ~/.codex/config.toml fragment:
model = "gpt-5.6-sol"
model_provider = "zukko"
[model_providers.zukko]
name = "zukko"
base_url = "https://api.zukko.pro/v1"
wire_api = "responses"How to pass the key and the remaining steps are in the Codex guide.
What to choose
- Your own code from scratch, any model:
/v1/chat/completions. It works with every model, GLM included, and with any OpenAI-compatible library. - A tool that requires Responses (Codex and similar):
/v1/responseswith a Claude or GPT model. - Code already written for Responses: keep it, unless you need GLM models.
Price does not depend on the endpoint: you pay for input and output tokens at the model's rate. More on the format is in the Chat Completions reference and the Python SDK guide. To try both APIs, create a key in the dashboard.
FAQ
I get a 404 on /responses. What is wrong?
Most often the base URL lacks /v1. The full address is https://api.zukko.pro/v1/responses; in the SDK set base_url="https://api.zukko.pro/v1" and it will append /responses itself.
I get 403 model_not_allowed_for_key. Is it the endpoint?
No. This code means the model is not in the key's list of allowed models. Add it in the key settings in the dashboard or use another key. The endpoint has nothing to do with it.
Does the price differ between endpoints?
No. Cost is based on input and output tokens and the model's price. The only difference is the usage field names: prompt_tokens/completion_tokens versus input_tokens/output_tokens.
Can I connect Codex to a GLM model?
No: Codex works only through the Responses API, and GLM models on Zukko are not available via /v1/responses. For Codex pick gpt-5.6-sol, gpt-5.6-luna, or a Claude model.
zukko
Pay 25×
less
for Claude Code & ChatGPT
One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.
Related articles
- Your Own Neural Network Proxy on Node.js: Keep the API Key Off the FrontendA small Express server between your frontend and the model API: the key stays on the server, and models, request size and rate are capped.
- DeepSeek API in Python: Requests, Streaming, JSON and Error HandlingReady code for DeepSeek V4.1 Flash and V4 Pro on the OpenAI SDK: a request, streaming, JSON parsing and careful error handling.
- Gemini API in Python: requests via the OpenAI SDK and OpenAI formatA working Gemini API example in Python with no extra library: change base_url in the OpenAI SDK, add streaming and JSON, and compare with google-genai.
- Anthropic SDK for Python: base_url, key and a first request via a gatewayThe official anthropic package for Python just needs a different address. We cover base_url, the key, streaming, token counting, caching and errors.