# Streaming chat

Streaming works on both surfaces. Set `stream: true` on
`/v1/chat/completions`, or use the Anthropic SDK's streaming helper against
`/v1/messages`. You get server-sent events back.

Every snippet below reads the key from `HYPHEN_API_KEY`:

```bash
export HYPHEN_API_KEY="sk-..."
```

## OpenAI surface, Python

```bash
pip install openai
```

```python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.hyphen-solution.com/v1",
    api_key=os.environ["HYPHEN_API_KEY"],
)

stream = client.chat.completions.create(
    model="minimax-m3",
    messages=[{"role": "user", "content": "Explain a Bloom filter in three sentences."}],
    max_tokens=2000,
    stream=True,
)

for chunk in stream:
    if not chunk.choices:
        continue
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)
print()
```

The `if not chunk.choices` guard matters. The final usage chunk can arrive with
an empty `choices` array, and indexing it blindly throws.

### Getting token counts out of a stream

Ask for them. The counts arrive in a final chunk after the content is done.

```python
stream = client.chat.completions.create(
    model="minimax-m3",
    messages=[{"role": "user", "content": "Explain a Bloom filter in three sentences."}],
    max_tokens=2000,
    stream=True,
    stream_options={"include_usage": True},
)

for chunk in stream:
    if chunk.usage:
        print("\n", chunk.usage)
    elif chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
```

## OpenAI surface, Node

```bash
npm install openai
```

```javascript
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.hyphen-solution.com/v1",
  apiKey: process.env.HYPHEN_API_KEY,
});

const stream = await client.chat.completions.create({
  model: "minimax-m3",
  messages: [{ role: "user", content: "Explain a Bloom filter in three sentences." }],
  max_tokens: 2000,
  stream: true,
});

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

Save as `stream.mjs` and run `node stream.mjs`. The `?.` chain does the same job
as the Python guard above.

## Anthropic surface, Python

The Anthropic SDK appends `/v1/messages` itself, so the base URL has **no
`/v1`** on it.

```bash
pip install anthropic
```

```python
import os
from anthropic import Anthropic

client = Anthropic(
    base_url="https://api.hyphen-solution.com",
    api_key=os.environ["HYPHEN_API_KEY"],
)

with client.messages.stream(
    model="minimax-m3",
    max_tokens=2000,
    system="You are terse. No preamble.",
    messages=[{"role": "user", "content": "Explain a Bloom filter in three sentences."}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
    final = stream.get_final_message()

print()
print(final.usage)
```

`stream.text_stream` yields only the text deltas. Use `stream.get_final_message()`
after the loop when you need the assembled message and the token counts.

## Anthropic surface, Node

```bash
npm install @anthropic-ai/sdk
```

```javascript
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  baseURL: "https://api.hyphen-solution.com",
  apiKey: process.env.HYPHEN_API_KEY,
});

const stream = client.messages.stream({
  model: "minimax-m3",
  max_tokens: 2000,
  messages: [{ role: "user", content: "Explain a Bloom filter in three sentences." }],
});

stream.on("text", (text) => process.stdout.write(text));

const final = await stream.finalMessage();
process.stdout.write("\n");
console.log(final.usage);
```

## Raw SSE with curl

Useful when you are debugging a proxy and want to see the wire format.

```bash
curl -N https://api.hyphen-solution.com/v1/chat/completions \
  -H "Authorization: Bearer $HYPHEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "minimax-m3",
    "messages": [{ "role": "user", "content": "Count to five." }],
    "max_tokens": 2000,
    "stream": true
  }'
```

`-N` disables curl's output buffering. Without it you will sit and stare at
nothing until the whole response lands. The stream ends with `data: [DONE]`.

## Nothing came out

If the stream finishes and you printed an empty string, you almost certainly
set `max_tokens` too low. The M-series are reasoning models and internal
reasoning spends the same budget as the answer. Use 2000 or more. See
[Choosing a model](/choosing-a-model#the-max_tokens-gotcha).

## Related

- [Tool calling](/recipes/tool-calling): functions on both surfaces.
- [Handling the 429 cap](/recipes/handling-429): what to do when the budget runs out.
- [API Reference](/api): every parameter.
