Streaming
Send "stream": true and the answer arrives in pieces as Server-Sent Events (SSE). The event format depends on the endpoint and matches the original API, so official SDKs parse it for you.
1Chat Completions
POST /v1/chat/completions sends data: lines with answer fragments and ends with data: [DONE].
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Hello"}}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":"stop"}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":12,"completion_tokens":3,"total_tokens":15}}
data: [DONE]For Claude and GPT models Zukko adds stream_options: {"include_usage": true} to the request itself. So the last chunk before [DONE] carries usage and an empty choices array. Code that reads chunk.choices[0] without a check will crash on it:
for chunk in stream:
if chunk.choices:
print(chunk.choices[0].delta.content or "", end="", flush=True)
elif chunk.usage:
print(f"\n[tokens: {chunk.usage.prompt_tokens} in, {chunk.usage.completion_tokens} out]")2Messages
POST /v1/messages sends Anthropic events: message_start, content_block_start, content_block_delta, content_block_stop, message_delta, and message_stop.
event: message_start
data: {"type":"message_start","message":{"id":"msg_...","role":"assistant","model":"claude-sonnet-5","content":[]}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":3}}
event: message_stop
data: {"type":"message_stop"}GLM models called through /v1/messages stream in the same format: Zukko converts their output into Anthropic events.
3Responses
POST /v1/responses sends named events: response.created, response.output_item.added, response.output_text.delta, response.output_item.done, response.completed, and others. The full list is on POST /v1/responses.
4Errors inside a stream
The 200 HTTP status is sent before the model starts answering. If an error happens later, it arrives as an event inside the stream — a data: line with an error field. Check the events, not just the response status.
A stream that carried only an error — no text and no usage — is not billed.
5Cost and interruption
- A streamed response has no
X-Zukko-Cost-MicrosorX-Fetch-Balance-Centsheaders: the cost is known only at the end. Look it up in GET /v1/usage or the dashboard. - If the client closes the connection early, Zukko still reads the model's answer to the end and charges for everything generated. Aborting a stream does not save money — use
max_tokensto cap spend. - Streamed responses are also covered by the cache and
Idempotency-Keyif they are no larger than 2 MB; the streamed and non-streamed versions of a request are stored separately.
6SDK examples
Ready-made streaming code is on the Python and Node.js pages.