Streaming
Set stream: true to receive tokens as they are generated. Topo streams server-sent events in the OpenAI format and closes with a usage chunk and the receipt.
POST/v1/chat/completions
Enable streaming
curl -N https://api.topolabs.ai/v1/chat/completions \ -H "Authorization: Bearer $TOPO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "claude-sonnet-5.5", "messages": [{ "role": "user", "content": "Explain zk proofs in 3 lines" }], "stream": true, "stream_options": { "include_usage": true } }'stream = client.chat.completions.create( model="claude-sonnet-5.5", messages=[{"role": "user", "content": "Explain zk proofs in 3 lines"}], stream=True, stream_options={"include_usage": True},) for chunk in stream: if chunk.choices: print(chunk.choices[0].delta.content or "", end="")const stream = await client.chat.completions.create({ model: "claude-sonnet-5.5", messages: [{ role: "user", content: "Explain zk proofs in 3 lines" }], stream: true, stream_options: { include_usage: true },}); for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.delta?.content ?? "");}Event format
Each event is a data: line carrying one JSON chunk. Lines that start with a colon are keepalive comments. The stream ends with data: [DONE].
event stream
sse
data: {"id":"chatcmpl_7Hq2kf91","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":"A"}}]} data: {"id":"chatcmpl_7Hq2kf91","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":" zk proof"}}]} : topo-keepalive data: {"id":"chatcmpl_7Hq2kf91","object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":18,"completion_tokens":406},"topo":{"node":"eu-west-2","ttft_ms":182,"receipt":{"amount":"0.0041","currency":"USDT"}}} data: [DONE]Usage and receipts
Set stream_options.include_usage to true to receive a final chunk with token counts. Topo adds the topo receipt to that same chunk, so you can log cost per request without a second call.
Keepalives and hedging
- Keepalive comments arrive every 15 seconds while a long reasoning step runs. Ignore them.
- If no token arrives within 600 ms, the router starts the request on a second node and streams whichever answers first. You are billed once.
- Streams close after 10 minutes without a token.