Streaming
The same endpoint, one token at a time.
Add "stream": true to a chat completions request and the response changes from one JSON object to a sequence of them, arriving as the model writes — the same server-sent-event format an OpenAI client already knows how to read.
/v1/chat/completionsThe frame format
Every event is one line starting with data: , holding one JSON object, followed by a blank line. The stream ends with a line that is the literal text [DONE] — not JSON, and not part of any chunk's shape:
data: {"id":"chatcmpl-8f...","object":"chat.completion.chunk","created":1755878402,"model":"larsa-general","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}
data: {"id":"chatcmpl-8f...","object":"chat.completion.chunk","created":1755878402,"model":"larsa-general","choices":[{"index":0,"delta":{"content":"One"},"finish_reason":null}]}
data: {"id":"chatcmpl-8f...","object":"chat.completion.chunk","created":1755878402,"model":"larsa-general","choices":[{"index":0,"delta":{"content":", two"},"finish_reason":null}]}
data: {"id":"chatcmpl-8f...","object":"chat.completion.chunk","created":1755878402,"model":"larsa-general","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
When reasoning_effort isn't "none" — see the chat reference before you rely on this — the same chunks can carry a reasoning_content delta instead of (or before) content, so a client that only reads content sees nothing until the model is done thinking, if it finishes at all.
Reconstructing the message
The full message is every chunk's delta.content concatenated in order. The first chunk usually carries delta.role; the last carries a non-null finish_reason and an empty delta — that's your signal the answer is complete, one event before [DONE] closes the connection.
stream and read usage from the single response, or reconcile it afterward against the billing API.Aborting
There's no cancel endpoint — a stream stops when you stop reading it. The server notices the connection is gone on its next write and stops generating; nothing about it is billed differently for having been cut short.
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000); // give up after 5s
const stream = await client.chat.completions.create(
{ model: "larsa-general", messages, stream: true },
{ signal: controller.signal },
);
stream = client.chat.completions.create(
model="larsa-general", messages=messages, stream=True,
)
for chunk in stream:
...
if enough_already:
stream.close() # closes the underlying connection
break
Talking plain HTTP, it's the same idea in whatever your language calls it: cancel the request's context in Go, drop the response in Rust, close the InputStream in Java, dispose the response message in C#, return the wrong byte count from CURLOPT_WRITEFUNCTION in PHP, or just break out of the read_body block in Ruby.
Full example
curl https://api.console.larsa.larsima.com/v1/chat/completions \
-N \
-H "Authorization: Bearer $LARSA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "larsa-general",
"stream": true,
"messages": [
{"role": "user", "content": "Count from one to five."}
],
"reasoning_effort": "none"
}'import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["LARSA_API_KEY"],
base_url="https://api.console.larsa.larsima.com/v1",
)
stream = client.chat.completions.create(
model="larsa-general",
messages=[{"role": "user", "content": "Count from one to five."}],
stream=True,
extra_body={"reasoning_effort": "none"},
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True)
print()
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.LARSA_API_KEY,
baseURL: "https://api.console.larsa.larsima.com/v1",
});
const stream = await client.chat.completions.create({
model: "larsa-general",
messages: [{ role: "user", content: "Count from one to five." }],
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}
process.stdout.write("\n");
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.LARSA_API_KEY,
baseURL: "https://api.console.larsa.larsima.com/v1",
});
async function main(): Promise<void> {
const stream = await client.chat.completions.create({
model: "larsa-general",
messages: [{ role: "user", content: "Count from one to five." }],
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}
process.stdout.write("\n");
}
main();
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
)
type chunk struct {
Choices []struct {
Delta struct {
Content string `json:"content"`
} `json:"delta"`
} `json:"choices"`
}
func main() {
reqBody := []byte(`{
"model": "larsa-general",
"stream": true,
"messages": [{"role": "user", "content": "Count from one to five."}],
"reasoning_effort": "none"
}`)
req, _ := http.NewRequest("POST",
"https://api.console.larsa.larsima.com/v1/chat/completions",
bytes.NewReader(reqBody))
req.Header.Set("Authorization", "Bearer "+os.Getenv("LARSA_API_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
scanner := bufio.NewScanner(res.Body)
for scanner.Scan() {
line := scanner.Text()
if !strings.HasPrefix(line, "data: ") {
continue
}
payload := strings.TrimPrefix(line, "data: ")
if payload == "[DONE]" {
break
}
var c chunk
if err := json.Unmarshal([]byte(payload), &c); err != nil {
continue
}
if len(c.Choices) > 0 {
fmt.Print(c.Choices[0].Delta.Content)
}
}
fmt.Println()
}
// Cargo.toml:
// [dependencies]
// reqwest = { version = "0.12", features = ["blocking"] }
// serde_json = "1"
use std::env;
use std::io::{BufRead, BufReader};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let api_key = env::var("LARSA_API_KEY")?;
let body = r#"{
"model": "larsa-general",
"stream": true,
"messages": [{"role": "user", "content": "Count from one to five."}],
"reasoning_effort": "none"
}"#;
let client = reqwest::blocking::Client::new();
let response = client
.post("https://api.console.larsa.larsima.com/v1/chat/completions")
.bearer_auth(api_key)
.header("Content-Type", "application/json")
.body(body)
.send()?;
let reader = BufReader::new(response);
for line in reader.lines() {
let line = line?;
let Some(payload) = line.strip_prefix("data: ") else { continue };
if payload == "[DONE]" {
break;
}
let chunk: serde_json::Value = serde_json::from_str(payload)?;
if let Some(delta) = chunk["choices"][0]["delta"]["content"].as_str() {
print!("{delta}");
}
}
println!();
Ok(())
}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.stream.Stream;
public class StreamingExample {
public static void main(String[] args) throws Exception {
String apiKey = System.getenv("LARSA_API_KEY");
String body = "{"
+ "\"model\": \"larsa-general\","
+ "\"stream\": true,"
+ "\"messages\": [{\"role\": \"user\", "
+ "\"content\": \"Count from one to five.\"}],"
+ "\"reasoning_effort\": \"none\""
+ "}";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.console.larsa.larsima.com/v1/chat/completions"))
.header("Authorization", "Bearer " + apiKey)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<Stream<String>> response =
client.send(request, HttpResponse.BodyHandlers.ofLines());
response.body().forEach(line -> {
if (!line.startsWith("data: ")) return;
String payload = line.substring(6);
if (payload.equals("[DONE]")) return;
// A minimal example prints the raw delta line; parse it with
// whatever JSON library your project already uses.
System.out.println(payload);
});
}
}
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
var apiKey = Environment.GetEnvironmentVariable("LARSA_API_KEY");
var payload = "{"
+ "\"model\": \"larsa-general\","
+ "\"stream\": true,"
+ "\"messages\": [{\"role\": \"user\", \"content\": \"Count from one to five.\"}],"
+ "\"reasoning_effort\": \"none\""
+ "}";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.console.larsa.larsima.com/v1/chat/completions")
{
Content = new StringContent(payload, Encoding.UTF8, "application/json")
};
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync();
if (line is null || !line.StartsWith("data: ")) continue;
var data = line["data: ".Length..];
if (data == "[DONE]") break;
using var doc = JsonDocument.Parse(data);
var delta = doc.RootElement.GetProperty("choices")[0].GetProperty("delta");
if (delta.TryGetProperty("content", out var contentEl))
{
Console.Write(contentEl.GetString());
}
}
Console.WriteLine();
<?php
$apiKey = getenv('LARSA_API_KEY');
$payload = json_encode([
'model' => 'larsa-general',
'stream' => true,
'messages' => [
['role' => 'user', 'content' => 'Count from one to five.'],
],
'reasoning_effort' => 'none',
]);
$buffer = '';
$ch = curl_init('https://api.console.larsa.larsima.com/v1/chat/completions');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_WRITEFUNCTION => function ($ch, $data) use (&$buffer) {
$buffer .= $data;
while (($pos = strpos($buffer, "\n")) !== false) {
$line = substr($buffer, 0, $pos);
$buffer = substr($buffer, $pos + 1);
if (!str_starts_with($line, 'data: ')) {
continue;
}
$chunkPayload = substr($line, 6);
if ($chunkPayload === '[DONE]') {
break;
}
$chunk = json_decode($chunkPayload, true);
echo $chunk['choices'][0]['delta']['content'] ?? '';
}
return strlen($data);
},
]);
curl_exec($ch);
curl_close($ch);
echo PHP_EOL;
require "net/http"
require "json"
require "uri"
api_key = ENV.fetch("LARSA_API_KEY")
uri = URI("https://api.console.larsa.larsima.com/v1/chat/completions")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{api_key}"
request["Content-Type"] = "application/json"
request.body = {
model: "larsa-general",
stream: true,
messages: [{ role: "user", content: "Count from one to five." }],
reasoning_effort: "none",
}.to_json
buffer = String.new
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request) do |response|
response.read_body do |fragment|
buffer << fragment
while (i = buffer.index("\n"))
line = buffer.slice!(0..i)
next unless line.start_with?("data: ")
payload = line.sub("data: ", "").strip
break if payload == "[DONE]"
chunk = JSON.parse(payload)
delta = chunk.dig("choices", 0, "delta", "content")
print delta if delta
end
end
end
end
puts