Build
Updated
Streaming
Set stream to true to receive server-sent events as the output is produced. The event vocabulary is the Open Responses one, with exactly one terminal event per stream.
Set stream: true on POST /v1/responses and the reply is a server-sent
events body instead of a single JSON object. Each event’s event: name is the
same as the type field inside its data: payload, which is what the official
SDKs’ stream parsers expect.
The Node and Python examples use a client set up for this API.
curl -N https://api.helping.io/v1/responses \ -H "Authorization: Bearer $HELPINGIO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "anthropic/claude-sonnet-5", "stream": true, "input": "Count from 1 to 5." }'const stream = await client.responses.create({ model: "anthropic/claude-sonnet-5", stream: true, input: "Count from 1 to 5.",});
for await (const event of stream) { if (event.type === "response.output_text.delta") { process.stdout.write(event.delta); } if (event.type === "response.completed") { console.log("\nusage:", event.response.usage); }}stream = client.responses.create( model="anthropic/claude-sonnet-5", stream=True, input="Count from 1 to 5.",)
for event in stream: if event.type == "response.output_text.delta": print(event.delta, end="", flush=True) elif event.type == "response.completed": print("\nusage:", event.response.usage)Items nest, and each level closes only once the level inside it has. A message holds a content part, which holds the text, so the text finishes first, then its part, then the item. A response of one text message is this, captured:
[ "response.created", "response.in_progress", "response.output_item.added", "response.content_part.added", "response.output_text.delta", "response.output_text.done", "response.content_part.done", "response.output_item.done", "response.completed"]Each of those names is an event: line with its data: payload underneath.
Items that hold no content part are shorter by two events: reasoning closes
with response.reasoning_text.done and then response.output_item.done, and a
function call closes with response.function_call_arguments.done and then
response.output_item.done. A response carrying several items opens and
closes them one at a time, between the same response.created and terminal
event.
A terminal event’s payload in full, captured from the same response, is one
response.completed event carrying the same response object a non-streaming
call returns:
{ "response": { "background": false, "completed_at": 1767225600, "conversation": null, "created_at": 1767225600, "error": null, "frequency_penalty": 0.0, "id": "resp_ZXhhbXBsZS9zdC9zdHJlYW0tdGVybWluYWwAAAAAAAAAAAAAAAAAAA", "incomplete_details": null, "instructions": null, "max_output_tokens": null, "max_tool_calls": null, "metadata": null, "model": "anthropic/claude-sonnet-5", "object": "response", "output": [ { "content": [ { "annotations": [], "logprobs": [], "text": "Streaming hello.", "type": "output_text" } ], "id": "msg_00000000000000000000000000000001", "role": "assistant", "status": "completed", "type": "message" } ], "parallel_tool_calls": true, "presence_penalty": 0.0, "previous_response_id": null, "prompt_cache_key": null, "reasoning": null, "safety_identifier": null, "service_tier": "default", "status": "completed", "store": true, "temperature": 1.0, "text": { "format": { "type": "text" } }, "tool_choice": "auto", "tools": [], "top_logprobs": 0, "top_p": 1.0, "truncation": "disabled", "usage": { "input_tokens": 100, "input_tokens_details": { "cache_write_tokens": 0, "cached_tokens": 0 }, "output_tokens": 20, "output_tokens_details": { "reasoning_tokens": 0 }, "total_tokens": 120 } }, "sequence_number": 8, "type": "response.completed"}Event vocabulary
Section titled “Event vocabulary”Every event carries a strictly increasing sequence_number, and
output_index is stable for the lifetime of an item.
| Event | Terminal | When |
|---|---|---|
response.created | The response exists. The payload carries the response object with an empty output | |
response.in_progress | The response is producing output | |
response.output_item.added | An output item (message, function call, reasoning) opened | |
response.content_part.added | A content part inside a message opened | |
response.output_text.delta | A chunk of assistant text | |
response.output_text.done | The complete text of that part | |
response.refusal.delta | The model declined and gave an explanation, which streams as the text of a refusal content part after any text already produced. Not sent when there is no explanation | |
response.refusal.done | The refusal part is complete and carries its text, which is empty when there is no explanation. The response then completes with response.completed | |
response.function_call_arguments.delta | A chunk of a function call's JSON arguments | |
response.function_call_arguments.done | The complete arguments for that call | |
response.reasoning_text.delta | A chunk of reasoning content, when the model produces reasoning | |
response.reasoning_text.done | The complete reasoning content | |
response.content_part.done | A content part inside a message closed | |
response.output_item.done | An output item closed | |
response.completed | Yes | The payload is the full response object, including usage |
response.failed | Yes | The response object carries error.code and error.message |
response.incomplete | Yes | The response stopped early, for example at max_output_tokens. Read incomplete_details.reason |
error | Yes | The final state was not known when the stream ended, with code response_unresolved. The response may still complete: retrieve it by id, or retry |
Reasoning events arrive before the text they precede, when the model produces reasoning at all.
Events you will not receive
Section titled “Events you will not receive”OpenAI’s Responses schema declares events beyond the ones above, and the published API reference carries the whole vocabulary, so a client generated from it can hold names this API never sends. Nothing below ever arrives, and a handler for one of them never runs. What you get instead is in the second column.
| Event | What you get instead |
|---|---|
response.queued | A stream opens with response.created and response.in_progress, so read response.created as the start |
response.output_text.annotation.added | Output text is not annotated here, so annotations on a text part is always empty |
response.reasoning_summary_part.added | Reasoning is not summarized. The reasoning item opens with response.output_item.added |
response.reasoning_summary_part.done | Reasoning is not summarized. The reasoning item closes with response.output_item.done |
response.reasoning_summary_text.delta | Reasoning is not summarized. The reasoning itself streams on response.reasoning_text.delta |
response.reasoning_summary_text.done | Reasoning is not summarized. The complete reasoning arrives on response.reasoning_text.done |
What a stream guarantees
Section titled “What a stream guarantees”- Exactly one terminal event. Every stream ends with one of
response.completed,response.failed,response.incomplete, orerror. Never a bare drop. A response that was canceled ends withresponse.failedwhose response hasstatus: "cancelled"and noerror, so read the status rather than assuming a failure. - A keep-alive every 15 seconds. While the stream is quiet you get an SSE comment frame, so proxies between you and the API do not time the connection out. Standard SSE parsers ignore comments.
- A 300 second ceiling. A
stream is closed after 300 seconds with its
terminal event. The response may not be finished at that point: retrieve it
with
GET /v1/responses/{response_id}. - Failures are in-band. Once the stream is open the HTTP status has
already been sent. A failure after that arrives as
response.failedwhoseerrorcarries the same codes and messages as a non-streaming error. A failure of the stream itself, rather than of the response, arrives as anerrorevent instead, which carriescode,messageandparamon the event itself rather than nested: readevent.message. Those codes are below. - Tool calls end the stream. When the model calls one of your function
tools, the stream emits the
function_callitem and then its terminal event, carrying that item on the response. You post the tool output as the next request. See Function calling.
What an error event can say
Section titled “What an error event can say”An error event is terminal and carries a code of its own, read the same
way as the code on an HTTP error:
code | Meaning |
|---|---|
response_unresolved | The stream ended before a result was ready. The response may still finish: retrieve it by the id response.created carried. Retrying without the same Idempotency-Key starts a second response. |
Everything else that can go wrong reaches you as a response object, on
response.failed or response.incomplete. See
Errors.