> ## Documentation Index
> Fetch the complete documentation index at: https://docs.naga.ac/llms.txt
> Use this file to discover all available pages before exploring further.

# Streaming

> Get the answer piece by piece as the model writes it, so your interface shows text before the model finishes.

Set `stream: true` on any chat endpoint and NagaAI sends the answer as server-sent events while the model writes it. Your interface can show the first words while the model is still writing the rest.

> [tab] Responses
>
> ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> import os
> from openai import OpenAI
>
> client = OpenAI(base_url="https://api.naga.ac/v1", api_key=os.environ["NAGA_API_KEY"])
>
> stream = client.responses.create(
>     model="deepseek-v4.1-flash",
>     input="Explain backpressure in three sentences.",
>     stream=True,
> )
>
> for event in stream:
>     if event.type == "response.output_text.delta":
>         print(event.delta, end="", flush=True)
>     elif event.type == "response.completed":
>         print("\n", event.response.usage)
> ```
>
> ```javascript Node.js theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> import OpenAI from "openai";
>
> const client = new OpenAI({ baseURL: "https://api.naga.ac/v1", apiKey: process.env.NAGA_API_KEY });
>
> const stream = await client.responses.create({
>   model: "deepseek-v4.1-flash",
>   input: "Explain backpressure in three sentences.",
>   stream: true,
> });
>
> for await (const event of stream) {
>   if (event.type === "response.output_text.delta") process.stdout.write(event.delta);
>   if (event.type === "response.completed") console.log("\n", event.response.usage);
> }
> ```
>
> ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> curl -N https://api.naga.ac/v1/responses \
>   -H "Authorization: Bearer $NAGA_API_KEY" \
>   -H "Content-Type: application/json" \
>   -d '{"model": "deepseek-v4.1-flash", "input": "Explain backpressure in three sentences.", "stream": true}'
> ```

> [tab] Chat Completions
>
> ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> import os
> from openai import OpenAI
>
> client = OpenAI(base_url="https://api.naga.ac/v1", api_key=os.environ["NAGA_API_KEY"])
>
> stream = client.chat.completions.create(
>     model="deepseek-v4.1-flash",
>     messages=[{"role": "user", "content": "Explain backpressure in three sentences."}],
>     stream=True,
>     stream_options={"include_usage": True},
> )
>
> for chunk in stream:
>     if chunk.choices and chunk.choices[0].delta.content:
>         print(chunk.choices[0].delta.content, end="", flush=True)
>     if chunk.usage:
>         print("\n", chunk.usage)
> ```
>
> ```javascript Node.js theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> import OpenAI from "openai";
>
> const client = new OpenAI({ baseURL: "https://api.naga.ac/v1", apiKey: process.env.NAGA_API_KEY });
>
> const stream = await client.chat.completions.create({
>   model: "deepseek-v4.1-flash",
>   messages: [{ role: "user", content: "Explain backpressure in three sentences." }],
>   stream: true,
>   stream_options: { include_usage: true },
> });
>
> for await (const chunk of stream) {
>   const text = chunk.choices[0]?.delta?.content;
>   if (text) process.stdout.write(text);
>   if (chunk.usage) console.log("\n", chunk.usage);
> }
> ```
>
> ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> curl -N https://api.naga.ac/v1/chat/completions \
>   -H "Authorization: Bearer $NAGA_API_KEY" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "model": "deepseek-v4.1-flash",
>     "messages": [{"role": "user", "content": "Explain backpressure in three sentences."}],
>     "stream": true,
>     "stream_options": {"include_usage": true}
>   }'
> ```

> [tab] Messages
>
> ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> import os
> from anthropic import Anthropic
>
> client = Anthropic(base_url="https://api.naga.ac", api_key=os.environ["NAGA_API_KEY"])
>
> with client.messages.stream(
>     model="claude-sonnet-5",
>     max_tokens=512,
>     messages=[{"role": "user", "content": "Explain backpressure in three sentences."}],
> ) as stream:
>     for text in stream.text_stream:
>         print(text, end="", flush=True)
>     print("\n", stream.get_final_message().usage)
> ```
>
> ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> import Anthropic from "@anthropic-ai/sdk";
>
> const client = new Anthropic({ baseURL: "https://api.naga.ac", apiKey: process.env.NAGA_API_KEY });
>
> const stream = client.messages.stream({
>   model: "claude-sonnet-5",
>   max_tokens: 512,
>   messages: [{ role: "user", content: "Explain backpressure in three sentences." }],
> });
>
> stream.on("text", (text) => process.stdout.write(text));
> const final = await stream.finalMessage();
> console.log("\n", final.usage);
> ```
>
> ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
> curl -N https://api.naga.ac/v1/messages \
>   -H "x-api-key: $NAGA_API_KEY" \
>   -H "Content-Type: application/json" \
>   -d '{
>     "model": "claude-sonnet-5",
>     "max_tokens": 512,
>     "messages": [{"role": "user", "content": "Explain backpressure in three sentences."}],
>     "stream": true
>   }'
> ```

The SDKs parse the events for you. With cURL you see them raw, one `data:` line per event, and the response has the `text/event-stream` content type.

## Stream fields by format

Each format names its events differently, but they carry the same things. Use this table to find the field you need:

| | Responses | Chat Completions | Messages |
| - | - | - | - |
| Text | `response.output_text.delta` | `choices[0].delta.content` | `content_block_delta` with `text_delta` |
| Tool arguments | `response.function_call_arguments.delta` | `choices[0].delta.tool_calls[].function.arguments` | `content_block_delta` with `input_json_delta` |
| Reasoning | `response.reasoning_summary_text.delta`, `response.reasoning_text.delta` | `choices[0].delta.reasoning_content` | `content_block_delta` with `thinking_delta` |
| Usage | In `response.completed` | In the last chunk, if you send `stream_options: {"include_usage": true}` | In `message_delta` |
| End of the stream | `response.completed`, `response.incomplete` or `response.failed`, with no `[DONE]` | A chunk with `finish_reason`, then `data: [DONE]` | `message_stop` |

Chat Completions sends usage only when you ask for it. That last chunk has an empty `choices` array, so check `chunk.choices` before you read from it, as the example does.

### Responses events

Every event has an `event:` line with its type and a `data:` line with a JSON object that repeats the type and carries a `sequence_number`. A text answer starts with `response.created`, then `response.output_item.added` and `response.content_part.added`. The text follows in many `response.output_text.delta` events, and `response.output_text.done`, `response.content_part.done` and `response.output_item.done` close it. The last event is `response.completed`, with the full response and its `usage`.

Tool calls add `response.function_call_arguments.delta` and `.done`, and web search adds `response.web_search_call.in_progress`, `.searching` and `.completed`. A stream that stops at `max_output_tokens` ends with `response.incomplete` instead of `response.completed`.

### Chat Completions chunks

Each `data:` line holds a `chat.completion.chunk`, and all chunks of one answer share one `id`. The first chunk carries `role: "assistant"` in `choices[0].delta`. Later ones carry `content`, `reasoning_content` on reasoning models, or `tool_calls`. A tool call arrives in pieces: the first piece has the `id` and `function.name`, and later pieces with the same `index` add to `function.arguments`.

The last content chunk has a `finish_reason` of `stop`, `length`, `tool_calls` or `content_filter`.

### Messages events

`message_start` opens the message with its `id` and `model`. Each block of the answer, whether `text`, `thinking` or `tool_use`, comes as `content_block_start`, then `content_block_delta` events, then `content_block_stop`. The deltas are `text_delta`, `thinking_delta`, `signature_delta` or `input_json_delta` for tool input. `message_delta` brings the `stop_reason` and the final `usage`, and `message_stop` ends the stream.

## When a provider fails

NagaAI holds back the `200` status until the first content is ready. If the provider it picked fails before that, inside its stream or before it, NagaAI sends the request to another provider that serves the model. You only get an error status when every attempt fails, and then it's a normal error response, not an error inside a stream.

Once content has started, the status is already sent, so a later failure arrives as an error event inside the stream. NagaAI doesn't charge for the broken stream. [Errors in a stream](https://docs.naga.ac/build/error-handling.md#errors-in-a-stream) shows the event in each format and how to retry.

## Closing the connection

The model keeps generating after your client disconnects, and NagaAI charges the full request. To limit the cost of answers you might abandon, set `max_output_tokens` in Responses or `max_tokens` in Chat Completions and Messages.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.