Chat Completions API
/ API docs← API quickstartView as Markdown
POST https://api.opencheese.dev/v1/chat/completions is the OpenAI Chat Completions API. The
OpenAI SDKs, OpenCode, Zed, Harness AI and most chat apps use it.
A first request
curl https://api.opencheese.dev/v1/chat/completions \
-H "Authorization: Bearer $OPENCHEESE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "MODEL_ID", "messages": [{"role": "system", "content": "Answer in one sentence."}, {"role": "user", "content": "What is a gateway?"}]}'
model and messages are required. Take the model ID from GET /v1/models.
The answer has the usual shape:
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"model": "MODEL_ID",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "A gateway passes your requests on to the models behind it." },
"finish_reason": "stop"
}
],
"usage": { "prompt_tokens": 24, "completion_tokens": 13, "total_tokens": 37 }
}
Parameters
| Field | Notes |
|---|---|
model |
Required. An id from /v1/models. |
messages |
Required. system, user, assistant and tool messages. |
stream |
true for server-sent events. |
tools, tool_choice |
Function calling, where the model supports tools. |
reasoning_effort |
A level from the model's supported_reasoning_levels. |
response_format |
Structured output, where the model supports it. |
temperature, top_p, max_tokens |
Optional. If you don't set them, the model's defaults are used. |
Streaming
curl -N https://api.opencheese.dev/v1/chat/completions \
-H "Authorization: Bearer $OPENCHEESE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "MODEL_ID", "stream": true, "messages": [{"role": "user", "content": "Count to three."}]}'
Each event is a chat.completion.chunk; the text arrives in choices[0].delta.content. Near
the end come the finish_reason and the usage, and the stream ends with data: [DONE]:
data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":"One"},"finish_reason":null}]}
data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":", two, three."},"finish_reason":null}]}
data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: {"object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":12,"completion_tokens":6,"total_tokens":18}}
data: [DONE]
Tool calls
curl https://api.opencheese.dev/v1/chat/completions \
-H "Authorization: Bearer $OPENCHEESE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "MODEL_ID", "messages": [{"role": "user", "content": "What is the weather in Paris?"}], "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Current weather for a city", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}]}'
When the model calls a tool, the reply's finish_reason is tool_calls and
message.tool_calls holds the call. Run the tool, then send the history back with a tool
message carrying the result:
{
"model": "MODEL_ID",
"messages": [
{ "role": "user", "content": "What is the weather in Paris?" },
{
"role": "assistant",
"content": null,
"tool_calls": [{ "id": "call_1", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\":\"Paris\"}" } }]
},
{ "role": "tool", "tool_call_id": "call_1", "content": "18 degrees, sunny" }
]
}
A reply cut off by the output limit in the middle of a tool call ends with
finish_reason: "length": treat its tool call as incomplete and do not run it.
Images in the prompt
Models whose modalities.input includes image read pictures sent as image_url parts, either
an https:// address or a data: URL:
{
"model": "MODEL_ID",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "What is in this picture?" },
{ "type": "image_url", "image_url": { "url": "data:image/png;base64,iVBORw0KGgo..." } }
]
}
]
}
Reasoning effort
Set reasoning_effort to one of the levels the model lists in supported_reasoning_levels
(reasoning.effort and Anthropic's output_config.effort are read too). If you don't set
one, the model's default is used.
curl https://api.opencheese.dev/v1/chat/completions \
-H "Authorization: Bearer $OPENCHEESE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "MODEL_ID", "reasoning_effort": "high", "messages": [{"role": "user", "content": "Plan a three-day trip to Lyon."}]}'
A level the model doesn't offer is refused with unsupported_reasoning_effort, and the error
lists the levels it does offer. Reasoning text, where the model returns it, arrives in
message.reasoning_content (delta.reasoning_content when streaming).
Image models in a chat
Choose an image model (its modalities.output is image) and the latest user message becomes
the image prompt. The picture arrives in message.images as a data: URL, and an image sent
in the conversation is edited where the model can edit. Image models answer one picture per
request; for every image parameter use the Image API.
Errors that arrive as a reply
Problems you need to act on, such as an invalid key, an exhausted plan or an empty balance,
come back as the assistant's message in a 200 response streamed as text/event-stream, even
when stream is false, so chat apps show them to you. Every other error is a normal HTTP
status with a JSON body. Check the response's Content-Type before parsing it as JSON; see
Errors and rate limits.
SDKs
Python:
from openai import OpenAI
client = OpenAI(base_url="https://api.opencheese.dev/v1", api_key="YOUR_OPENCHEESE_API_KEY")
stream = client.chat.completions.create(
model="MODEL_ID",
messages=[{"role": "user", "content": "Write a haiku about cheese."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
JavaScript:
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.opencheese.dev/v1", apiKey: process.env.OPENCHEESE_API_KEY });
const reply = await client.chat.completions.create({
model: "MODEL_ID",
messages: [{ role: "user", content: "Write a haiku about cheese." }]
});
console.log(reply.choices[0].message.content);
To keep a conversation's requests together in the dashboard, add the
opencheese-session-uuid header.