Streaming
Set stream to true and read Server-Sent Events to receive the first token sooner.
On this page
Returning a long answer in one shot leaves users staring at a blank screen for seconds. With streaming, each fragment is pushed as it is generated and time-to-first-token typically drops to a few hundred milliseconds.
Turning it on
curl https://api.apione.ai/v1/chat/completions \
-H "Authorization: Bearer $APIONE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"stream": true,
"messages": [{ "role": "user", "content": "Write a 200-word product intro" }]
}'Wire format
The response Content-Type is text/event-stream. Each chunk is one data: line, and the stream ends with data: [DONE]:
data: {"id":"chatcmpl-01J8Z","choices":[{"delta":{"role":"assistant"},"index":0}]}
data: {"id":"chatcmpl-01J8Z","choices":[{"delta":{"content":"APIONE"},"index":0}]}
data: {"id":"chatcmpl-01J8Z","choices":[{"delta":{"content":" is a"},"index":0}]}
data: {"id":"chatcmpl-01J8Z","choices":[{"delta":{},"finish_reason":"stop","index":0}],
"usage":{"prompt_tokens":18,"completion_tokens":126,"total_tokens":144}}
data: [DONE]- Text increments live in
choices[0].delta.content; concatenate them in order. usageappears only on the final content chunk — don't break early if you need billing data.- Keep-alive comment lines (starting with
:) may appear; ignore them.
With the SDKs
stream = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[{"role": "user", "content": "Write a 200-word product intro"}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta
if delta.content:
print(delta.content, end="", flush=True)const stream = await client.chat.completions.create({
model: "claude-sonnet-4-6",
messages: [{ role: "user", content: "Write a 200-word product intro" }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}Errors mid-stream
The HTTP status is fixed once headers are sent, so a failure after the stream starts arrives as an error chunk instead:
data: {"type":"error","error":{"message":"upstream timed out","type":"upstream_error","code":"upstream_timeout","retryable":true}}Text already shown to the user cannot be taken back. Keep what was rendered, surface an “interrupted” state, and let the user decide whether to retry — silently resending the whole request bills you twice.
Gotchas
- Reverse proxies (Nginx and friends) must have response buffering disabled, or chunks are batched.
- Don't use
EventSourcein the browser — it is GET-only and cannot carry a body. Usefetchand read theReadableStream. - Serverless platforms often cap responses at 30s; confirm yours supports streaming before shipping long answers.
Contact us
Something unclear, or need a human? Pick the channel that fits your question.
Supportsupport@apione.ai
WeCom
Telegram
Communityhttps://t.me/apione
Support tickets
Billing & accountConsole › Feedback