Logo

Complete developer guide and best practices to help you get started quickly.

Advanced

SSE streaming

Both translation and speech recognition endpoints support stream=true. This section gathers the five event types, a client-side parser example, mid-stream error handling, and the key differences between ASR and translation streams.

In SSE (Server-Sent Events) mode, the API does not wait for the model to finish before responding — it splits the result into a sequence of events. The response Content-Type is text/event-stream; each event starts with data: <JSON> and terminates with a blank line (\n\n). Events flow as head → chunk* → tail → meta; any failure replaces remaining events with an error event and closes the stream.

Event structure

The table below summarises the order and payload of all five event types. head, tail, and meta are mandatory (exactly one per stream); chunk appears zero or more times; error may surface at any moment and closes the stream once it does.

Streaming event reference

EventOrderRequiredData shapeDescription
head
1translation: { source_lang: string } asr: { detected_language: string }Stream-start event with the source/detected language. Translation uses source_lang; speech recognition uses detected_language (field name differs).
chunk
2..NstringOutput text chunk; data holds the chunk string. Zero or more. Concatenate in arrival order to rebuild the full output.
tail
N+1translation: (empty) asr: { segments?: Segment[] }End-of-output marker. Translation has no data; speech recognition may include segments (when the client requested timestamp_format).
meta
N+2translation: { usage } asr: { usage; vocabulary_used; forced_replacement_count }Metadata event. Translation carries only usage (token counts); speech recognition additionally carries vocabulary_used and forced_replacement_count (the latter is always 0 in SSE).
error
any{ code; message; details?; request_id? }Business error event; may appear at any time and closes the stream. The code matches the JSON-mode error code; request_id can be added to issue reports.
Response headers include Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive, and X-Accel-Buffering: no. Every event terminates with a blank line (\n\n).

Client-side parsing

Two parsing paths are available: the browser's standard EventSource (GET only) or fetch + ReadableStream (supports POST + custom headers — required here, since this API is POST-only). The example below uses the fetch path: read the stream, split events by the blank-line delimiter, JSON.parse the data: line, and switch on type.

Parsing SSE with fetch + ReadableStream + TextDecoder. Note the buffer.split + buffer.pop pattern that handles partial events spanning network reads.

javascript
const res = await fetch("https://abemono.abestar.com.tw/api/v1/translations/text", {
    method: "POST",
    headers: {
        "X-API-Key": process.env.ABESTAR_API_KEY,
        "Content-Type": "application/json",
        Accept: "text/event-stream",
    },
    body: JSON.stringify({
        text: "今天的會議改到下午三點。",
        target_language: "en",
        stream: true,
    }),
});

const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let output = "";

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

    // Events are separated by a blank line; the last partial event stays in the buffer.
    const events = buffer.split("\n\n");
    buffer = events.pop() ?? "";

    for (const evt of events) {
        const dataLine = evt.split("\n").find((l) => l.startsWith("data: "));
        if (!dataLine) continue;
        const payload = JSON.parse(dataLine.slice(6));

        switch (payload.type) {
            case "head":
                console.log("source_lang:", payload.data.source_lang);
                break;
            case "chunk":
                output += payload.data;
                break;
            case "tail":
                console.log("output:", output);
                break;
            case "meta":
                console.log("usage:", payload.data.usage);
                break;
            case "error":
                console.error(payload.data.code, payload.data.request_id);
                break;
        }
    }
}

Mid-stream error handling

When an error or timeout occurs mid-stream, the server emits an error event and closes the stream. Chunks already received remain valid — the client decides whether to keep the partial result or discard everything. The error code matches the JSON-mode code (translation_failed, asr_timeout, content_policy_violation, and so on).

Example mid-stream error event. Pull the code and request_id fields as the starting point for diagnosis.

json
data: {
  "type": "error",
  "data": {
    "code": "translation_failed",
    "message": "Translation service temporarily unavailable",
    "details": {},
    "request_id": "0af7651916cd43dd8448eb211c80319c"
  }
}

SSE business errors still come back with HTTP 200

Once the streaming connection is established, HTTP status stays at 200. Model errors and timeouts are delivered as error events instead of rewriting the status. Inspect the event payload (payload.type === "error") to determine success or failure — do not rely on the HTTP status alone.

Differences between ASR and translation

  • head field name differs: translation uses source_lang, speech recognition uses detected_language.
  • tail payload differs: translation's tail has no data; speech recognition's tail carries segments when timestamp_format is specified.
  • meta payload differs: translation's meta carries only usage; speech recognition's meta also includes vocabulary_used and forced_replacement_count.
  • Error code prefix differs: translation uses translation_*, speech recognition uses asr_* (asr_failed, asr_timeout, ...).
  • Both endpoints are mutually exclusive with forced_replacement: sending stream=true and forced_replacement_dictionary_id together returns 400 validation_error before the stream starts.