Core
Chat completions
The endpoint the product exists for. Every OpenAI parameter is forwarded verbatim, and anything genuinely unsupported is rejected by name rather than dropped.
POST /v1/chat/completions
The request and response are OpenAI’s. Field names are theirs and are not tidied, because the whole migration story is that an unmodified client works.
Parameters
| Field | Notes |
|---|---|
model | Required. A model id or an alias. See Models |
messages | Required. Roles system, user, assistant, tool. content is a string, or an array of parts for images |
max_tokens | Defaults to 1024, ceiling 32000. The reservation is priced off this |
max_completion_tokens | OpenAI’s newer spelling for the same cap. Send either one. Sending both with different values is a 400 |
temperature, top_p, stop, seed | Forwarded. Ranges are OpenAI’s |
frequency_penalty, presence_penalty | Forwarded |
stream, stream_options | See Streaming |
tools, tool_choice | See Tool calling |
response_format | See Structured output |
reasoning_effort | none, low, medium, high. See Models |
logprobs, top_logprobs | Forwarded |
prompt_cache_key | See Prompt caching |
The response
{
"id": "chatcmpl-8f2a1c",
"object": "chat.completion",
"created": 1785097284,
"model": "llama-3.1-8b",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "The capital of Kenya is Nairobi." },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 21,
"completion_tokens": 8,
"total_tokens": 29,
"prompt_tokens_details": { "cached_tokens": 0 }
},
"dawnstack": { "cost_micros": 2, "cost_usd": 0.000002, "request_id": "req_8f2a1c" }
}
dawnstack is additive and non-standard. An OpenAI client ignores unknown fields, so it costs you
nothing, and it means you do not have to reconcile a call against your ledger afterwards to know
what it cost. cost_micros is the integer of record; cost_usd is the same number in dollars.
finish_reason is passed through from the model. stop means it finished, length means it hit
max_tokens, tool_calls means it wants you to call a function, and error means the stream
failed after it had started.
Multi-turn
Send the whole conversation. There is no server-side state, no thread id, and nothing to clean up.
messages = [{"role": "system", "content": "Answer in one sentence."}]
while True:
messages.append({"role": "user", "content": input("> ")})
reply = client.chat.completions.create(model="llama-3.1-8b", messages=messages)
text = reply.choices[0].message.content
messages.append({"role": "assistant", "content": text})
print(text)Set prompt_cache_key to the same value across a conversation’s turns and the repeated prefix gets
cheaper. See Prompt caching.
Images
Some models accept images. Send content as an array of parts instead of a string, exactly as you
would against OpenAI:
{
"model": "gemma-3-27b",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "What is in this picture?" },
{ "type": "image_url", "image_url": { "url": "https://example.com/photo.jpg" } }
]
}
]
}
Check input_modalities on GET /v1/models before you send one. A model that
reads images publishes ["text", "image"]; everything else publishes ["text"]. Sending an image
to a text-only model is a 400 naming messages, before any billable work.
input_modalities is the field to filter on, not modality. modality says which endpoint serves
the model and therefore what its price is counted in, so every chat model is "text" there whether
or not it reads images.
Images are estimated generously when your balance is reserved, and the reservation is refunded down to the real figure once the supplier reports actual usage. You are billed on that figure, never on the estimate.
What is not supported
n > 1 is not implemented: ask twice. Sending n: 1 is fine and is what we already do. Everything
else that is missing is on Not supported yet.