Streaming responses across the three route shapes

stream: true on all three, each in its own event format. What the proxy does before the first byte, and why aborting does not make a request free.

Same flag, three event formats

Streaming is requested the same way everywhere: set stream: true in the request body. What comes back differs by route, because each route speaks its own event format and the proxy does not flatten them into a common one.

On /v1/messages you get the Anthropic event sequence — message_start, content_block_delta, message_delta, message_stop. On /v1/chat/completions you get chat.completion.chunk frames terminated by data: [DONE]. On /v1/responses you get the Responses event stream. Each is what the matching SDK already knows how to iterate.

The response body is passed through rather than buffered, so time-to-first-token is not traded away for the proxy's own bookkeeping. Your request body, by contrast, is read in full before forwarding — it has to be, to determine the model and price the request.

Streaming on each of the three shapes. The flag is the same; the frames are not.
# Anthropic events on /v1/messages
curl -N https://token-share.app/v1/messages \
  -H "x-api-key: $TOKEN_SHARE_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-5",
    "max_tokens": 512,
    "stream": true,
    "messages": [{"role": "user", "content": "Count to five slowly."}]
  }'

# chat.completion.chunk frames, terminated by [DONE]
curl -N https://token-share.app/v1/chat/completions \
  -H "Authorization: Bearer $TOKEN_SHARE_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "grok-4.6",
    "stream": true,
    "messages": [{"role": "user", "content": "Count to five slowly."}]
  }'

# Responses event stream
curl -N https://token-share.app/v1/responses \
  -H "Authorization: Bearer $TOKEN_SHARE_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "gpt-5.6-terra",
    "stream": true,
    "input": "Count to five slowly."
  }'

What happens before the first byte

Once a streaming response is returned, its status line is committed and cannot be changed. So the proxy waits for the first meaningful chunk from the upstream before committing. An upstream that opens 200 text/event-stream and then closes without emitting anything is answered 502 instead of reaching you as an unparseable success you cannot retry.

A newline heartbeat does not count as that first chunk — it is retained so a successful stream is byte-for-byte intact, but it is not treated as evidence that an answer has begun. Only a chunk carrying a non-whitespace byte commits the response.

That single-chunk wait is the whole buffering cost. Nothing further is held back, so the rest of the body flows as fast as the upstream produces it.

Aborting, and what a mid-stream failure looks like

Usage accounting reads the stream on its own branch, split from the one your client consumes. That branch keeps draining after your client disconnects, so aborting just before the final usage event does not hand you the answer for free — the tokens already produced are still counted.

When a provider resets mid-stream, whatever was read before the reset is retained, so a positive usage report that arrived before the failure is still billable. Where no usage was reported at all, the request is recorded as unbilled rather than charged at a guess.

A failure after the status line is committed can only be reported inside the stream, because the 200 is already on the wire. On a stable-* lane a pre-commit failure is emitted as an error event in your route's own format — an Anthropic error event, an OpenAI error frame followed by [DONE], or a response.failed event — so a client mid-iteration ends cleanly instead of hanging on a stream that has already failed.

Common questions

Does streaming cost more or less than a single response?
The same. Billing is per token from the usage the stream reports, using the same rates and the same multiplier as a non-streaming call. Streaming changes when you see the tokens, not what they cost.
My stream ended without a terminal event. What should my client do?
Treat a stream that ends without its terminal frame as a failed turn and retry it, rather than committing the partial text as an answer. On a stable lane the translator synthesizes a terminator when the upstream cuts off, so an SDK waiting on message_stop or [DONE] is not left hanging — but a client that consumes the raw stream should still handle the case.
Can I get usage counts out of a stream?
Yes — the usage arrives in the stream's own terminal frames, in whatever form your route uses. On the chat-completions shape an OpenAI SDK with stream_options.include_usage finds it in a final choice-less chunk before [DONE]. The gateway reads its own copy independently, so what you see and what you are billed for come from the same numbers.