DocsStreaming

Streaming

API reference

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].

text
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:

python
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.

text
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-Micros or X-Fetch-Balance-Cents headers: 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_tokens to cap spend.
  • Streamed responses are also covered by the cache and Idempotency-Key if 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.

Streaming · Zukko Docs