Skip to main content
Streaming works the way it works on the API this one is shaped after. Set "stream": true in the body, read the events, stop at data: [DONE]. If your client already streams from an OpenAI-compatible endpoint, it streams from this one without a change.

Asking for a stream

The response has Content-Type: text/event-stream. The body is relayed as it arrives: nothing is buffered on our side, so the first token reaches you as soon as the model produces it.

On the Responses API

POST /v1/responses streams too, and everything on this page about how a stream is relayed, where the counts ride and how a stream can end early applies to it unchanged, because it is the same request path. What differs is the transcript. A Responses stream carries named events with their own shapes rather than the chat completion chunks shown below, and those frames are the provider’s, forwarded as they arrive. Read them against the Responses format rather than against the transcript in the next section. The one place the two are identical is the ending: the counts arrive on the same :x-uniblock-usage comment line, and the codes that can cut a stream short are the same codes.

What the transcript looks like

Annotated. Ids and content are illustrative; the framing is exact.
Everything except the :x-uniblock-usage line is the standard format. That line is a comment: the server-sent events specification requires a parser to ignore any line beginning with a colon, so SDKs and browsers skip it and your streaming loop never sees it. It sits immediately before data: [DONE] and never after, because the counts are final only at that point and a client that stops reading at the terminator would otherwise miss it. A streamed response carries Uniblock’s own token counts as a server-sent events comment line, written immediately before data: [DONE]:
The usage line
A line beginning with a colon is a comment the server-sent events specification requires a parser to ignore, so your SDK does not see it and nothing in your streaming loop needs to change. Read it only if you want it.
Counts not reported
Fields are omitted rather than set to zero when a provider did not report them, so “the provider did not say” stays distinguishable from “nothing was used”. reported: true with zeros means zero. An absent field means unknown. The counts are the provider’s own. Nothing is re-counted or estimated. A stream that is cut off carries no usage line, because there are no final counts to report. On a response that is not streamed there is no separate counts line. The provider’s own usage object inside the JSON body is what you read, exactly as you would from OpenAI. Either way, the figures we settle your balance against are the ones on your Activity screen. Absent counts are omitted rather than sent as zero, and "reported": false carries no counts at all. The full field list is in Headers.

Getting the counts in the stream itself

The comment line above is ours. If you want token counts as part of the stream your SDK parses, ask for them the standard way:
You then get the usual final chunk with an empty choices array and a usage object, ahead of data: [DONE]. If you do not ask for it, you do not receive it, because the commonest streaming loop in every language reads chunk.choices[0].delta and throws on a chunk that has no choices.

When a stream ends early

A streamed response has already sent its status and headers by the time anything can go wrong, so a failure cannot arrive as a status code. Instead the stream ends with one more data: line carrying the standard error body, and then closes. There is no data: [DONE] after it.
Three codes can end a stream: balance_exhausted only ever appears mid-stream. A request refused before it started because the account cannot pay is a 402 with insufficient_credit, so “never started” and “cut off part way” are always distinguishable. Neither is charged as a whole request: a refusal costs nothing, and a cut stream costs what it produced. Handle a stream that stops without data: [DONE] and without an error line as an incomplete response. It means the connection ended, which is a truncated answer rather than a complete one, and we record it as a stream that did not finish rather than as a fault.

What streaming does not change

  • Money works the same way. Credit is held before the request goes out, and a streamed request is metered and appears in your usage like any other.
  • Errors use the same body and the same codes as a non-streamed request. Only the channel differs, and only because a status code cannot be recalled.
  • The bytes are the provider’s. We add the one comment line and relay everything else exactly as it arrived.