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

# Streaming

> Receive a chat completion as it is generated, and handle errors during a stream.

Set `stream: true` on a [chat completions](/docs/chat-completions) request to receive the response as server-sent events.

## Event format

* The response has the content type `text/event-stream`.
* Each event's `data` is a JSON object with `object: "chat.completion.chunk"`. The new text is in `choices[].delta.content`.
* Reasoning arrives in `delta.reasoning` and `delta.reasoning_details`. Tool call arguments arrive in `delta.tool_calls`.
* The stream ends with the line `data: [DONE]`.

## Usage on the final chunk

<ParamField body="stream_options.include_usage" type="boolean">
  `true` adds a final chunk with `usage`, sent before `data: [DONE]`. Without it, the stream has no usage data. Billing is the same either way.
</ParamField>

## Read a stream

<CodeGroup>
  ```python Python theme={null}
  import os
  from openai import OpenAI

  client = OpenAI(
      api_key=os.environ["FORTYTWO_API_KEY"],
      base_url="https://api.fortytwo.network/v1",
  )

  model_id = "qwen/qwen3.8-27b"

  stream = client.chat.completions.create(
      model=model_id,
      messages=[{"role": "user", "content": "Describe the tide in three sentences."}],
      stream=True,
      stream_options={"include_usage": True},
  )

  usage = None

  for chunk in stream:
      if chunk.usage is not None:
          usage = chunk.usage
      for choice in chunk.choices:
          if choice.delta.content:
              print(choice.delta.content, end="", flush=True)

  print()
  print(usage)
  ```

  ```typescript TypeScript theme={null}
  import OpenAI from "openai";

  const client = new OpenAI({
    apiKey: process.env.FORTYTWO_API_KEY,
    baseURL: "https://api.fortytwo.network/v1",
  });

  const modelId = "qwen/qwen3.8-27b";

  const stream = await client.chat.completions.create({
    model: modelId,
    messages: [{ role: "user", content: "Describe the tide in three sentences." }],
    stream: true,
    stream_options: { include_usage: true },
  });

  let usage;

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

  console.log();
  console.log(usage);
  ```
</CodeGroup>

## Keep-alives and timeouts

* While no data is ready, the stream sends a keep-alive comment at least every 30 seconds. A comment is a line that starts with `:`. Skip these lines in a custom parser.
* Set the client read timeout above 30 seconds. A shorter timeout can close a stream that is still working.
* A request can run for up to 60 minutes — see [Rate limits](/docs/limits#how-long-a-request-may-run). A stream that reaches the limit ends with the error event below.

## Errors during a stream

If the request fails before the first event, the response is an ordinary error with an HTTP status and a JSON body — see [Errors](/docs/errors).

If it fails after the stream started, the last event is an error, and the stream closes. Every failure at this point, a timeout included, has the code `upstream_error`:

```json theme={null}
{
  "error": {
    "message": "upstream result unavailable",
    "type": "server_error",
    "code": "upstream_error"
  },
  "request_id": "req_…"
}
```

A stream that ends without `data: [DONE]` and without an error event lost its connection. A stopped stream cannot be resumed; a new request is charged separately — see [What is billed](/docs/pricing#what-is-billed). The `x-request-id` response header identifies the request for [support](/docs/support).


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