Responses API
NewPOST /v1/responses takes instructions and a list of input items and returns a response object made of output items, in the OpenAI Responses format. Use it with the responses API of the OpenAI SDK and with tools built on it, such as Codex CLI. The endpoint keeps no state: send the whole conversation with every request.
POST https://api.shannon-ai.com/v1/responses
The smallest request is a model id and a text. The reply is not streamed unless you ask for it.
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.shannon-ai.com/v1",
)
response = client.responses.create(
model="shannon-3",
input="Say hello in one sentence.",
)
print(response.output_text) import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.shannon-ai.com/v1",
});
const response = await client.responses.create({
model: "shannon-3",
input: "Say hello in one sentence.",
});
console.log(response.output_text); curl https://api.shannon-ai.com/v1/responses \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "shannon-3",
"input": "Say hello in one sentence."
}' The reply is a response object. Its output holds the reasoning, when the model reasoned, and the message:
{
"id": "resp_5f0c1e7a9b3d4c62a8e1f07d2b46c9a3",
"object": "response",
"created_at": 1791625200,
"model": "shannon-3",
"status": "completed",
"error": null,
"incomplete_details": null,
"instructions": null,
"metadata": {},
"parallel_tool_calls": true,
"temperature": null,
"tool_choice": "auto",
"tools": [],
"top_p": null,
"output": [
{
"id": "rs_8a2e6c1f4b7d4e03b9c5a1d70f3e2b64",
"type": "reasoning",
"summary": [
{
"type": "summary_text",
"text": "The user wants a greeting in one sentence. Keep it short and friendly."
}
]
},
{
"id": "msg_1c7b9e3a5d2f4a86b0e4c8d19a6f7e25",
"type": "message",
"role": "assistant",
"status": "completed",
"content": [
{
"type": "output_text",
"text": "Hello, it is good to meet you.",
"annotations": []
}
]
}
],
"usage": {
"input_tokens": 1184,
"output_tokens": 46,
"total_tokens": 1230
}
} Request fields
model and input are what a request needs. The Applied by column names the models on which a field changes the reply. The hosted open-weight models are the twelve ids of the model list; the Shannon 3 family is shannon-3, shannon-3-pro, shannon-3.1 and shannon-3.1-pro. Models & pricing
| Field | Type | Default | Description | Applied by |
|---|---|---|---|---|
model | string | Required. An id from the model list. Matching is not case-sensitive. A missing or unpublished id returns 400 unknown model. | All models | |
input | string | array | A string, which is read as one user message, or an array of input items. See Input items below. | All models | |
instructions | string | Instructions for the model, the system prompt of this format. | Hosted open-weight models, shannon-1.6-*, shannon-2-*, shannon-coder-1 | |
stream | boolean | false | true sends the reply as server-sent events. See Streaming below. | All models |
max_output_tokens | integer | 4096 | Upper limit of the reply, in tokens. A value outside 1 to 65,536 is moved into that range. It is also the amount set aside from your balance while the request runs. See Output limit below. | Hosted open-weight models, shannon-1.6-*, shannon-2-*, shannon-coder-1 |
temperature | number | Sampling temperature. On the hosted open-weight models values are kept between 0 and 2. | Hosted open-weight models; shannon-1.6-*, shannon-2-* and shannon-coder-1 on replies that are not streamed | |
top_p | number | Nucleus sampling. Values are kept between 0 and 1. | Hosted open-weight models | |
tools | array | The functions the model may call, in the flat form of this format: {"type": "function", "name", "description", "parameters"}. See Tools below. | All models | |
tool_choice | string | object | auto | "auto" lets the model decide. "required" makes it call a tool. {"type": "function", "name": "…"} makes it call that tool. | Hosted open-weight models |
reasoning | object | {"effort": "…"} with off, low, medium or high. Any other value returns 400. | Hosted open-weight models | |
text | object | {"format": {…}} asks for a JSON answer. See Structured output below. | All Shannon tiers; hosted open-weight models as listed per id |
previous_response_id, store, metadata, parallel_tool_calls, truncation, include, user and prompt_cache_key are accepted so that existing client code runs unchanged. They do not change the reply. Because no conversation is stored, a follow-up request carries the earlier turns in input.
Input items
An input item is either a message, with a role and a content, or a typed item that carries a tool call or its result. content is a string or an array of parts.
| Item | Description | Applied by |
|---|---|---|
{"role": "user", "content": …} | What you ask. content is a string, or input_text and input_image parts. | All models |
{"role": "assistant", "content": …} | An earlier reply of the model. content is a string, or output_text parts. | All models |
{"role": "system", "content": …}{"role": "developer", "content": …} | Instructions inside the conversation. | Hosted open-weight models |
{"type": "function_call", "call_id": "…", "name": "…", "arguments": "…"} | A tool call the model made in an earlier reply. arguments is a JSON string. | All models |
{"type": "function_call_output", "call_id": "…", "output": "…"} | The result of that call. call_id is the id of the call and output the result as a string. | All models |
Put instructions in the instructions field. With a Shannon 3 family id, put instructions that must hold into the user message.
The items of a reply's output can go back into input as they are: message and function_call items are read, reasoning items are skipped.
Content parts
| Part | Description | Available on |
|---|---|---|
{"type": "input_text", "text": "…"} | Text of a user message. | All models |
{"type": "output_text", "text": "…"} | Text of an assistant message. | All models |
{"type": "input_image", "image_url": "…"} | An image, as a data: URL with base64 content or as an http(s) URL. | Shannon 3 family, shannon-1.6-lite, shannon-1.6-pro, and the hosted open-weight models that list image input |
{"type": "input_file", "file_data": "data:application/pdf;base64,…"} | A document (PDF, Word, PowerPoint or Excel): file_data holds a data: URL, or file_url an http(s) URL. | Shannon 3 family |
On the Shannon tiers images and files are read from the last user item of input. Sizes and limits have their own page. Images and files
A request with instructions and options
This request sends instructions, a conversation as input items, an output limit, a temperature and a reasoning effort. It uses a hosted open-weight model, which applies all of them.
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.shannon-ai.com/v1",
)
response = client.responses.create(
model="DeepSeek-V4-Flash-0731-W4A16-AUTOROUND-REAP",
instructions="You are a physics teacher. Answer in two sentences.",
input=[
{"role": "user", "content": "Why is the sky blue?"},
{"role": "assistant", "content": "Air scatters blue light more than red light."},
{"role": "user", "content": "And why are sunsets red?"},
],
max_output_tokens=512,
temperature=0.3,
reasoning={"effort": "low"},
)
for item in response.output:
if item.type == "reasoning":
print("reasoning:", item.summary[0].text)
elif item.type == "message":
print("answer:", item.content[0].text)
print(response.usage) import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.shannon-ai.com/v1",
});
const response = await client.responses.create({
model: "DeepSeek-V4-Flash-0731-W4A16-AUTOROUND-REAP",
instructions: "You are a physics teacher. Answer in two sentences.",
input: [
{ role: "user", content: "Why is the sky blue?" },
{ role: "assistant", content: "Air scatters blue light more than red light." },
{ role: "user", content: "And why are sunsets red?" },
],
max_output_tokens: 512,
temperature: 0.3,
reasoning: { effort: "low" },
});
for (const item of response.output) {
if (item.type === "reasoning") console.log("reasoning:", item.summary[0].text);
if (item.type === "message") console.log("answer:", item.content[0].text);
}
console.log(response.usage); curl https://api.shannon-ai.com/v1/responses \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "DeepSeek-V4-Flash-0731-W4A16-AUTOROUND-REAP",
"instructions": "You are a physics teacher. Answer in two sentences.",
"input": [
{"role": "user", "content": "Why is the sky blue?"},
{"role": "assistant", "content": "Air scatters blue light more than red light."},
{"role": "user", "content": "And why are sunsets red?"}
],
"max_output_tokens": 512,
"temperature": 0.3,
"reasoning": {"effort": "low"}
}' The reply of a hosted open-weight model has id, object, model, created_at, status, output and usage. Its usage adds the tokens read from cache and the tokens spent on reasoning:
{
"usage": {
"input_tokens": 58,
"input_tokens_details": {
"cached_tokens": 0
},
"output_tokens": 67,
"output_tokens_details": {
"reasoning_tokens": 19
},
"total_tokens": 125
}
} The response object
id, object, status, output and usage are on every reply. Build your code on those.
| Field | Type | Description | Sent by |
|---|---|---|---|
id | string | resp_ followed by 32 hexadecimal characters. | All models |
object | string | Always response. | All models |
status | string | completed. In a stream it is in_progress first, and failed in a response.failed event. | All models |
output | array | The output items in the order the model produced them: reasoning, message, function_call. | All models |
usage | object | input_tokens, output_tokens and total_tokens. | All models |
usage.input_tokens_details | object | cached_tokens: the input tokens read from the prompt cache. | Hosted open-weight models |
usage.output_tokens_details | object | reasoning_tokens: the output tokens spent on reasoning. | Hosted open-weight models |
model | string | The canonical id of the model that answered. | Shannon 3 family, hosted open-weight models |
created_at | integer | Time of the reply, in Unix seconds. | Shannon 3 family, hosted open-weight models |
instructions, tools, temperature, … | instructions, tools and temperature repeat the request. error and incomplete_details are null, metadata is {}, parallel_tool_calls is true, tool_choice is auto and top_p is null. | Shannon 3 family |
Output items
| Item | Description |
|---|---|
{"id": "rs_…", "type": "reasoning", "summary": [{"type": "summary_text", "text": "…"}]} | The model's reasoning as one summary_text part. Present only when the model reasoned. |
{"id": "msg_…", "type": "message", "role": "assistant", "content": [{"type": "output_text", "text": "…"}]} | The answer: one output_text part with the text. |
{"id": "fc_…", "type": "function_call", "call_id": "call_…", "name": "…", "arguments": "…"} | A tool call. arguments is a JSON string. Send the result back with the same call_id. |
The OpenAI SDKs join the text of the message for you in response.output_text.
Streaming
Set stream to true for server-sent events. Each frame has an event: line with the name of the event and a data: line with a JSON object; the object repeats the name in type. Match deltas to their item by output_index.
With the OpenAI SDK, read the events by their type:
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.shannon-ai.com/v1",
)
stream = client.responses.create(
model="shannon-3",
input="Explain server-sent events in three sentences.",
stream=True,
)
for event in stream:
kind = getattr(event, "type", "error") # an event without a type is an error event
if kind == "response.reasoning_summary_text.delta":
pass # a piece of reasoning: event.delta
elif kind == "response.output_text.delta":
print(event.delta, end="", flush=True)
elif kind == "response.output_item.done" and event.item.type == "function_call":
print("\ntool call:", event.item.name, event.item.arguments)
elif kind == "response.completed":
print("\n", event.response.usage)
elif kind in ("response.failed", "error"):
raise RuntimeError("the stream failed, send the request again") import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.shannon-ai.com/v1",
});
const stream = await client.responses.create({
model: "shannon-3",
input: "Explain server-sent events in three sentences.",
stream: true,
});
for await (const event of stream) {
if (event.type === "response.reasoning_summary_text.delta") {
// a piece of reasoning: event.delta
} else if (event.type === "response.output_text.delta") {
process.stdout.write(event.delta);
} else if (event.type === "response.output_item.done" && event.item.type === "function_call") {
console.log("\ntool call:", event.item.name, event.item.arguments);
} else if (event.type === "response.completed") {
console.log("\n", event.response.usage);
} else if (event.type === "response.failed" || event.type === "error") {
throw new Error("the stream failed, send the request again");
}
} curl -N https://api.shannon-ai.com/v1/responses \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "shannon-3",
"input": "Explain server-sent events in three sentences.",
"stream": true
}' The events arrive in this order. The groups for reasoning, the message and tool calls appear only when the reply has them.
| Event | Payload | When |
|---|---|---|
response.created | response | Always first. response.status is in_progress and response.output is empty. |
response.in_progress | response | Always second, with the same object. |
response.output_item.added | output_index, item | The reasoning starts: a reasoning item with an empty summary. |
response.reasoning_summary_text.delta | output_index, summary_index, delta | A piece of the reasoning. |
response.reasoning_summary_text.done | output_index, summary_index, text | The reasoning is complete: its whole text. |
response.output_item.done | output_index, item | The finished reasoning item. |
response.output_item.added | output_index, item | The answer starts: a message item with status in_progress and no content. |
response.content_part.added | output_index, content_index, part | An empty output_text part opens. |
response.output_text.delta | output_index, content_index, delta | A piece of the answer. |
response.output_text.done | output_index, content_index, text | The answer is complete: its whole text. |
response.content_part.done | output_index, content_index, part | The finished output_text part. |
response.output_item.done | output_index, item | The finished message item, with status completed. |
response.output_item.added | output_index, item | A tool call starts: a function_call item with call_id, name and empty arguments. |
response.function_call_arguments.delta | output_index, delta | The arguments of the call, complete in one delta. |
response.function_call_arguments.done | output_index, arguments | The same arguments, as arguments. |
response.output_item.done | output_index, item | The finished function_call item. |
response.completed | response | Always last on success: the whole response object with output and usage. |
event: response.created
data: {"type":"response.created","response":{"id":"resp_5f0c…","object":"response","status":"in_progress","output":[], …}}
event: response.in_progress
data: {"type":"response.in_progress","response":{"id":"resp_5f0c…","object":"response","status":"in_progress","output":[], …}}
event: response.output_item.added
data: {"type":"response.output_item.added","output_index":0,"item":{"id":"msg_1c7b…","type":"message","role":"assistant","status":"in_progress","content":[]}}
event: response.content_part.added
data: {"type":"response.content_part.added","output_index":0,"content_index":0,"part":{"type":"output_text","text":""}}
event: response.output_text.delta
data: {"type":"response.output_text.delta","output_index":0,"content_index":0,"delta":"Server-sent"}
event: response.output_text.delta
data: {"type":"response.output_text.delta","output_index":0,"content_index":0,"delta":" events let a server push text over one HTTP reply."}
event: response.output_text.done
data: {"type":"response.output_text.done","output_index":0,"content_index":0,"text":"Server-sent events let a server push text over one HTTP reply."}
event: response.content_part.done
data: {"type":"response.content_part.done","output_index":0,"content_index":0,"part":{"type":"output_text","text":"Server-sent events let a server push text over one HTTP reply."}}
event: response.output_item.done
data: {"type":"response.output_item.done","output_index":0,"item":{"id":"msg_1c7b…","type":"message","role":"assistant","status":"completed","content":[{"type":"output_text","text":"Server-sent events let a server push text over one HTTP reply.","annotations":[]}]}}
event: response.completed
data: {"type":"response.completed","response":{"id":"resp_5f0c…","object":"response","status":"completed","output":[…],"usage":{"input_tokens":1190,"output_tokens":18,"total_tokens":1208}, …}} A stream that fails
On the Shannon tiers a failed reply ends with response.failed in place of response.completed. Its response.status is failed and response.error holds a code, rate_limit_exceeded or server_error, and a message.
On the hosted open-weight models a failure is an error event with code api_error and a message. response.completed still follows it.
Treat a stream as failed when it holds a response.failed or an error event, and send the request again.
Tools
A tool is a function your code runs. Describe it in tools; when the model wants it, the reply holds a function_call item in place of, or next to, the message.
A tool definition in tools:
[
{
"type": "function",
"name": "get_weather",
"description": "Current weather for a city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string"
}
},
"required": [
"city"
]
}
}
] The output of a reply that calls it:
[
{
"id": "call_3d9a7c1e5b2f4a60c8e1d7f09b24a6c5",
"type": "function_call",
"name": "get_weather",
"call_id": "call_3d9a7c1e5b2f4a60c8e1d7f09b24a6c5",
"arguments": "{\"city\":\"Paris\"}"
}
] Run the function, then send the request again with the call and its result added to input:
[
{
"role": "user",
"content": "What is the weather in Paris?"
},
{
"type": "function_call",
"call_id": "call_3d9a7c1e5b2f4a60c8e1d7f09b24a6c5",
"name": "get_weather",
"arguments": "{\"city\":\"Paris\"}"
},
{
"type": "function_call_output",
"call_id": "call_3d9a7c1e5b2f4a60c8e1d7f09b24a6c5",
"output": "{\"temperature_c\": 18, \"sky\": \"cloudy\"}"
}
] Only tools of type function are read. On shannon-1.6-*, shannon-2-* and shannon-coder-1 the parameters schema is reduced to the common JSON Schema keywords: oneOf is read as anyOf, and keywords such as $ref, additionalProperties, const, default and title are not passed to the model.
tool_choice values required and a named function are applied by the hosted open-weight models.
The tool loop is described in full on its own page. Function Calling
Structured output
This format spells structured output as text.format. The schema sits directly in format, next to name and strict, not under a json_schema member as on Chat Completions.
{"type": "json_object"}asks for any JSON object.{"type": "json_schema", "name", "schema", "strict"}asks for JSON that follows your schema.strictistruewhen you leave it out.
{
"model": "shannon-1.6-lite",
"input": "Extract the city and the date: 'The conference opens in Lisbon on 12 May 2027.'",
"text": {
"format": {
"type": "json_schema",
"name": "event",
"strict": true,
"schema": {
"type": "object",
"properties": {
"city": {
"type": "string"
},
"date": {
"type": "string",
"description": "ISO 8601 date"
}
},
"required": [
"city",
"date"
]
}
}
}
} The JSON arrives as text in the output_text part of the message; parse it yourself. A hosted open-weight model without structured output returns 400.
{"city": "Lisbon", "date": "2027-05-12"} Which model takes which form is listed on its own page. Structured Outputs
Reasoning
A model that reasons returns its reasoning as an output item of type reasoning, before the message. In a stream it arrives as response.reasoning_summary_text.delta events.
The hosted open-weight models apply reasoning.effort: off, low, medium or high, with high when you send nothing. off returns no reasoning item. Send it as an object; a bare string is not read on this endpoint.
The efforts are described on their own page. Reasoning effort
Output limit
max_output_tokens is 4,096 when you do not send it. It is the number of tokens set aside from your balance when the request starts; when the reply is complete, that amount is replaced by what the request used. A value that does not fit into your remaining balance returns 429 Quota exceeded.
It limits the length of the reply on shannon-1.6-*, shannon-2-* and shannon-coder-1, and the length of the answer text on the hosted open-weight models.
On this endpoint shannon-coder-1 is charged in tokens from your balance, like every other model.
A request body can be up to 32 MiB. Limits and balance
Errors
Errors use the OpenAI error body: an error object with type and message; some also carry code and param with the value null. The table lists what this endpoint returns most often. The full list, with what to retry, has its own page. Error Handling
| Status | Type | Message | When |
|---|---|---|---|
401 | authentication_error | Missing authenticationInvalid API key | No API key was sent, or the key is unknown or revoked. |
400 | invalid_request_error | unknown model: <id> | model is missing or is not a published id. |
400 | invalid_request_error | No user message provided. | Shannon tiers: input holds no user text. |
400 | invalid_request_error | <id> does not accept image input | An image was sent to a hosted open-weight model without image input. |
400 | invalid_request_error | <id> does not accept response_format | text.format was sent to a hosted open-weight model without structured output. |
400 | invalid_request_error | unknown reasoning effort '<value>'; expected off, low, medium or high | reasoning.effort holds a value outside the list. |
422 | invalid_request_error | Failed to deserialize the JSON body into the target type: … | The body is valid JSON but a field has the wrong type. |
429 | rate_limit_error | Quota exceeded. Upgrade your plan at shannon-ai.com/plan | max_output_tokens is larger than what is left of your balance. |
429 | rate_limit_error | Too many requests. Retry in <n>s. | Flood protection: more than 120 requests in one minute on your account. |
500 | api_error | The model backend failed to answer. Please retry. | The model did not produce a reply. Send the request again. |
502 | api_error | The model backend failed to answer. Please retry. | The same, on the hosted open-weight models. |
504 | api_error | upstream timed out | A hosted open-weight model took longer than the time limit of a reply that is not streamed. Send it with stream set to true. |