Streaming¶
Set "stream": true to receive results as the engine decodes them, instead of
waiting for the whole response. This is recommended: the production engine is
CPU-only, so a full response can take seconds to tens of seconds.
{
"stream": true,
"schema": {"total": {"type": "number", "integer": true}, "paid": "bool"},
"input": {"text": "Total 33000. Paid in cash."}
}
All checks that can fail before the engine runs — authentication, rate limit,
request validation — still return a normal JSON error (4xx/5xx) before
the stream starts. Once the stream is open, failures arrive as an error event.
Two wire formats¶
The events are the same; the encoding depends on the Accept header:
Accept |
Content-Type | Format |
|---|---|---|
| default | application/x-ndjson |
one JSON object per line |
text/event-stream |
text/event-stream |
Server-Sent Events |
Streaming responses carry Cache-Control: no-cache and X-Accel-Buffering: no
so proxies do not buffer them.
Events¶
Only four event types are ever sent: field.delta, field.done,
extraction.done, and error. Every event has a type field.
| Event | Payload | Meaning |
|---|---|---|
field.delta |
{type, field, value} |
A preview of a field as it is being written. Sent only when the preview changed. |
field.done |
{type, field, value} |
A field finished; value is the final typed value. Sent once per field. |
extraction.done |
full Extraction + {type} |
The complete result — same fields as Extract, including id and usage. Always sent, even for fields that never appeared earlier. |
error |
{type, error} |
Something failed after streaming started. error is a {type, code, message} object. |
Ordering: all field.delta events for a step come before its field.done
events. A field's field.done always carries the typed value; field.delta
values are raw text previews (a number may show as "3" before it becomes
33000).
Example sequence (NDJSON)¶
{"type":"field.delta","field":"total","value":"3"}
{"type":"field.delta","field":"total","value":"33000"}
{"type":"field.done","field":"paid","value":true}
{"type":"field.done","field":"total","value":33000}
{"type":"extraction.done","id":"req_01J…","object":"extraction","result":{"total":33000,"paid":true},"usage":{"input_tokens":41,"output_tokens":8,"fields":2,"images":0},…}
You can watch this with curl:
curl -N https://api.trusttyped.com/v1/extract \
-H "Authorization: Bearer tt_live_…" \
-H "Content-Type: application/json" \
-d '{"stream": true, "schema": {"total": {"type": "number", "integer": true}}, "input": {"text": "Total 33000."}}'
For SSE, add -H "Accept: text/event-stream"; each event is then framed as
event: <type> followed by data: <json>.
Reading the stream¶
import json, httpx
body = {"stream": True, "schema": {"total": {"type": "number", "integer": True}},
"input": {"text": "Total 33000. Paid in cash."}}
with httpx.stream(
"POST", "https://api.trusttyped.com/v1/extract",
headers={"Authorization": "Bearer tt_live_…"},
json=body, timeout=120,
) as resp:
resp.raise_for_status()
for line in resp.iter_lines():
if not line:
continue
event = json.loads(line)
if event["type"] == "field.delta":
print("…", event["field"], event["value"])
elif event["type"] == "field.done":
print("✓", event["field"], "=", event["value"])
elif event["type"] == "extraction.done":
print("final:", event["result"], event["usage"])
elif event["type"] == "error":
raise RuntimeError(event["error"])
const resp = await fetch("https://api.trusttyped.com/v1/extract", {
method: "POST",
headers: { Authorization: "Bearer tt_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
stream: true,
schema: { total: { type: "number", integer: true } },
input: { text: "Total 33000. Paid in cash." },
}),
});
const reader = resp.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
for (;;) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
let nl;
while ((nl = buffer.indexOf("\n")) >= 0) {
const ev = JSON.parse(buffer.slice(0, nl));
buffer = buffer.slice(nl + 1);
if (ev.type === "extraction.done") console.log("final", ev.result);
else if (ev.type === "error") throw new Error(ev.error.code);
else console.log(ev.type, ev.field, ev.value);
}
}
SSE event names
In SSE mode the event: line repeats the type (e.g. field.done), and
data: holds the same JSON. The JSON always contains type too, so the
same parser works for both formats.
Idempotency is not supported with streaming¶
Idempotency-Key cannot be combined with "stream": true; the request is
rejected with 400 idempotency_not_supported_for_stream. See
Idempotency.