Streaming
All three protocols support streaming, with a text/event-stream response. For the Anthropic and OpenAI formats, set "stream": true in the request body; for the Gemini native format, call the :streamGenerateContent endpoint instead.
Event format per protocol
| Protocol | Chunk shape | End marker |
|---|---|---|
Anthropic (/v1/messages) |
Paired event: + data: lines, in the sequence message_start → content_block_start → one or more content_block_delta → content_block_stop → message_delta → message_stop |
The message_stop event |
OpenAI (/v1/chat/completions) |
data: {...} chunks with object set to chat.completion.chunk; text is in choices[0].delta.content |
data: [DONE] |
Gemini (:streamGenerateContent) |
data: {...} chunks; text is in candidates[0].content.parts[].text |
The connection closes when the stream ends; there is no separate end marker |
OpenAI format, for example:
curl https://api.moyiapi.com/v1/chat/completions \
-H "Authorization: Bearer sk-moyiapi-xxxxxx" \
-H "content-type: application/json" \
-d '{
"model": "gpt-5.6-sol",
"stream": true,
"messages": [{"role": "user", "content": "Write one sentence"}]
}'
The final usage chunk
All three protocols report token usage inside the stream with no extra parameters:
| Protocol | Where usage appears |
|---|---|
| Anthropic | usage on the message_delta event (input_tokens, output_tokens, cache_read_input_tokens, and so on). The event also carries two extra fields, x_conv_id (the request's conversation ID, useful when reporting an issue) and x_is_final |
| OpenAI | The last chunk before [DONE]: choices is an empty array and usage is present (prompt_tokens, completion_tokens, total_tokens, and completion_tokens_details) |
| Gemini | usageMetadata on each chunk (promptTokenCount, candidatesTokenCount, and so on) |
Stream cancellation
Close the HTTP connection to stop receiving; the server stops pushing.
Handling errors during streaming
Errors before the stream starts (authentication failures, unavailable models, and so on) are not returned as an event stream: the HTTP status is not 200, content-type is application/json, and the error body matches the non-streaming shape. Once the stream has started, the HTTP status is already 200, so errors appear only inside the event stream and must be parsed according to the protocol in use.