MODEL CAPABILITIES
Streaming
Show the answer as it is written, and read the cost from the last frame.
Set stream: true and read the response as server-sent events. The first text arrives quickly, so interfaces can show progress instead of waiting for the full answer. Streaming is supported on Z1, Z1 Code, H1 and Project ZERO.
STREAMING COMPLETION
curl -N https://api.tessen.ai/v1/chat/completions \ -H "Authorization: Bearer $TESSEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "z1", "stream": true, "max_tokens": 256, "messages": [{"role": "user", "content": "Say hello in five words."}] }'import jsonimport osimport requests with requests.post( "https://api.tessen.ai/v1/chat/completions", headers={"Authorization": f"Bearer {os.environ['TESSEN_API_KEY']}"}, json={ "model": "z1", "stream": True, "max_tokens": 256, "messages": [{"role": "user", "content": "Say hello in five words."}], }, stream=True, timeout=120,) as response: response.raise_for_status() for line in response.iter_lines(decode_unicode=True): if not line.startswith("data: "): continue data = line[len("data: "):] if data == "[DONE]": break frame = json.loads(data) if "error" in frame: # a refusal arrives as a frame inside a 200 stream raise RuntimeError(frame["error"]["message"]) if frame["choices"]: print(frame["choices"][0]["delta"].get("content", ""), end="", flush=True) else: print() print(frame["usage"])const response = await fetch("https://api.tessen.ai/v1/chat/completions", { method: "POST", headers: { Authorization: `Bearer ${process.env.TESSEN_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ model: "z1", stream: true, max_tokens: 256, messages: [{ role: "user", content: "Say hello in five words." }], }),});if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`); const decoder = new TextDecoder();let buffer = "";for await (const chunk of response.body) { buffer += decoder.decode(chunk, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop(); for (const line of lines) { if (!line.startsWith("data: ")) continue; const data = line.slice("data: ".length); if (data === "[DONE]") continue; const frame = JSON.parse(data); if (frame.error) throw new Error(frame.error.message); // a refusal arrives as a frame inside a 200 stream if (frame.choices.length) process.stdout.write(frame.choices[0].delta.content ?? ""); else console.log("\n", frame.usage); }}WHAT ARRIVES
data: {"id":"85fef9589fba4d84af7a76e0a4581c6b","object":"chat.completion.chunk","created":1793282257,"model":"z1","choices":[{"index":0,"delta":{"content":"o"},"finish_reason":null}]} data: {"id":"85fef9589fba4d84af7a76e0a4581c6b","object":"chat.completion.chunk","created":1793282257,"model":"z1","choices":[{"index":0,"delta":{"content":"k"},"finish_reason":"stop"}]} data: {"id": "85fef9589fba4d84af7a76e0a4581c6b", "object": "chat.completion.chunk", "created": 1793282257, "model": "z1", "choices": [], "usage": {"prompt_tokens": 11, "completion_tokens": 2, "total_tokens": 13, "cost_usd": 2.85e-05, "cost_provenance": "family_price", "rate_id": "commercial-v1-2026-10-25"}} data: [DONE]Three things to handle
- Error frames. A refusal (for example 402 or 400) arrives as
data: {"error": {...}}inside an HTTP 200 stream, withtypewhere a plain response hascode. Check forerrorbefore readingchoices. - The usage frame. The last data frame has
choices: []andusage; on Tessen's families it includescost_usd, the settled cost. Do not treat the emptychoicesas a failure. - The terminator. The stream ends with
data: [DONE].
Note
Reasoning models spend output tokens thinking before the first answer token, and that reasoning is not streamed. H1 can pause before its first text appears.