Skip to content

Streaming Responses

For a responsive, chat-like experience, stream the model's reply as it's generated instead of waiting for the whole thing. Add "stream": true to a chat completion request and the response is delivered as Server-Sent Events (SSE).

POST https://api.blaick.ai/api/v1/chat/completions
Content-Type: application/json
Accept: text/event-stream

{ "model_id": "...", "messages": [...], "stream": true }

The event stream

Each event is a line beginning with data: followed by a JSON object. Every object has a type field. There are four types:

type When Payload
delta Repeatedly, as text is generated content — the next chunk of text to append
usage Once, near the end input_tokens, output_tokens, total_tokens, cost_tokens
done Once, at the very end auto_routed, routing_reason, routing_task_type
error If something goes wrong mid-stream message

A complete stream looks like this:

data: {"type": "delta", "content": "The"}
data: {"type": "delta", "content": " capital"}
data: {"type": "delta", "content": " of France is Paris."}
data: {"type": "usage", "input_tokens": 18, "output_tokens": 8, "total_tokens": 26, "cost_tokens": 52}
data: {"type": "done", "auto_routed": false, "routing_reason": null, "routing_task_type": null}

To assemble the full reply, concatenate the content of every delta event in order. When you receive usage, you have the final token cost. When you receive done, the response is complete.

Handle error events

An error can arrive mid-stream (for example, an upstream provider dropping the connection) as {"type": "error", "message": "..."}. Treat it as a terminal event and surface the message.

Consuming the stream

Use -N to disable buffering so events print as they arrive:

curl -N https://api.blaick.ai/api/v1/chat/completions \
  -H "Authorization: Bearer $BLAICK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model_id": "claude-sonnet-4-20250514",
    "messages": [{"role": "user", "content": "Write a haiku about the sea."}],
    "stream": true
  }'
import json, os, requests

BASE = "https://api.blaick.ai/api/v1"
headers = {"Authorization": f"Bearer {os.environ['BLAICK_API_KEY']}"}

with requests.post(
    f"{BASE}/chat/completions",
    headers=headers,
    json={
        "model_id": "claude-sonnet-4-20250514",
        "messages": [{"role": "user", "content": "Write a haiku about the sea."}],
        "stream": True,
    },
    stream=True,
) as resp:
    resp.raise_for_status()
    full = ""
    for line in resp.iter_lines(decode_unicode=True):
        if not line or not line.startswith("data:"):
            continue
        event = json.loads(line[len("data:"):].strip())
        if event["type"] == "delta":
            full += event["content"]
            print(event["content"], end="", flush=True)
        elif event["type"] == "usage":
            print(f"\n\nCost: {event['cost_tokens']} tokens")
        elif event["type"] == "done":
            break
        elif event["type"] == "error":
            raise RuntimeError(event["message"])
const BASE = "https://api.blaick.ai/api/v1";

const resp = await fetch(`${BASE}/chat/completions`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BLAICK_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model_id: "claude-sonnet-4-20250514",
    messages: [{ role: "user", content: "Write a haiku about the sea." }],
    stream: true,
  }),
});

const reader = resp.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let full = "";

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });

  const lines = buffer.split("\n");
  buffer = lines.pop(); // keep the last, possibly-incomplete line

  for (const line of lines) {
    if (!line.startsWith("data:")) continue;
    const event = JSON.parse(line.slice(5).trim());
    if (event.type === "delta") {
      full += event.content;
      process.stdout.write(event.content);
    } else if (event.type === "usage") {
      console.log(`\n\nCost: ${event.cost_tokens} tokens`);
    } else if (event.type === "error") {
      throw new Error(event.message);
    }
  }
}

Buffer across chunks

Network chunks don't align to event boundaries — a single delta line may be split across two reads. Buffer incoming bytes and only parse complete lines (as the JavaScript example does by holding back the last split segment).

WebSocket streaming

For bidirectional, real-time chat, connect to the WebSocket endpoint instead:

WSS wss://api.blaick.ai/api/v1/chat/ws/{conversation_id}

Authenticate by passing your token as a query parameter, then send and receive JSON messages:

const ws = new WebSocket(
  `wss://api.blaick.ai/api/v1/chat/ws/${conversationId}?token=${apiKey}`
);

ws.onopen = () => {
  ws.send(JSON.stringify({
    type: "message",
    content: "Hello!",
    model_id: "claude-sonnet-4-20250514",
  }));
};

ws.onmessage = (evt) => {
  const msg = JSON.parse(evt.data);
  if (msg.type === "delta") process.stdout.write(msg.content);
  else if (msg.type === "usage") console.log("\nCost:", msg.cost_tokens);
  else if (msg.type === "done") console.log("\n[complete]");
  else if (msg.type === "error") console.error(msg.message);
};

The server sends the same delta / usage / done / error message types. WebSocket is best when you want a persistent, low-latency channel for an ongoing conversation; plain SSE over POST is simpler for one-off completions.