Core

Streams

Server-sent events on the three formats, and the cost in a chat stream. What we charge when a stream stops or when you close it.

Send stream: true to get the answer in parts, as server-sent events. This works on the three endpoints that cost money.

EndpointThe events
/v1/chat/completionsOpenAI chat.completion.chunk frames, then data: [DONE]
/v1/messagesThe Anthropic events (message_start to message_stop), as the model sends them
/v1/responsesThe OpenAI Responses events (response.created to response.completed), as the model sends them

We do not change the events of a Messages stream or a Responses stream. The rest of this page is about the chat completions stream.

A chat completions stream

stream = client.chat.completions.create(
    model="claude-haiku-4-5-20251001",
    messages=[{"role": "user", "content": "Count to five."}],
    stream=True,
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
const stream = await client.chat.completions.create({
  model: "claude-haiku-4-5-20251001",
  messages: [{ role: "user", content: "Count to five." }],
  stream: true,
});

for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

The stream starts with a frame that has the role. Then the content frames come. The last choice frame has the finish_reason:

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"One"},"finish_reason":null}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]

Token counts and cost in a chat stream

To get the token counts, send stream_options:

stream = client.chat.completions.create(
    model="claude-haiku-4-5-20251001",
    messages=[{"role": "user", "content": "Count to five."}],
    stream=True,
    stream_options={"include_usage": True},
)

Then the stream has one more frame after the last choice frame. Its choices list is empty:

data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":9,"total_tokens":23},"kontinent":{"request_id":"req_8f2a1c","cost_micros":12,"cost_usd":0.000012,"list_price_usd":0.000059,"saved_usd":0.000047}}

The kontinent block on this frame shows the cost of the request. It is the same amount that we take from your balance. If your code reads chunk.choices[0] on each frame, add a check for an empty list.

Tool calls in a chat stream

Tool calls come in the stream. delta.tool_calls has an index. The first frame of each call has the id, the type and the function name. The next frames add parts of function.arguments. The SDKs join these parts for you. See Tool calls.

The model’s thinking in a chat stream

A reasoning model thinks before it answers, and you pay for those tokens. By default the stream does not send the thinking. To get it, send include_reasoning: true. Each part of the thinking then comes in delta.reasoning_content, apart from delta.content. An OpenAI SDK ignores this field, so the answer that your code reads does not change.

When the model’s supplier gives the number, the usage frame shows the thinking’s part of the output tokens in completion_tokens_details.reasoning_tokens.

Keep-alive comments

A model can be quiet for a long time while it thinks. If a chat stream sends no data for 15 seconds, we send this comment line:

: keep-alive

A line that starts with a colon is a comment in server-sent events. The OpenAI SDKs ignore it. If you read the stream with your own code, ignore each line that does not start with data: . Some proxies close a quiet connection, and the comment prevents this. Messages and Responses streams have their own ping events, so we do not add the comment to them.

When a stream stops

After a stream starts, we cannot send an HTTP status. If the model fails after that point, the last frame of a chat stream has finish_reason: "error", and there is no usage frame. Look at finish_reason. If you look only at the content, you cannot see the difference between a full answer and a part of an answer.

We charge for the output that the model wrote before the stop. See Reliability.

When you close the connection

After you close the connection, the request has 15 seconds to complete. If it completes in that time, we charge for the token count of the model. If it does not complete, we stop it. Then we charge for the output that the model wrote, and we give back the rest of the amount that we held. That count is never more than your max_tokens value.

A stream costs the same as the same request without a stream.

Streams and idempotency

A request with stream: true and an Idempotency-Key header gets a 400 error. To repeat a stream from a cache, we would have to keep each response, so we refuse the header. See Idempotency.