Logo

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

Tutorial

Speech recognition

Walks through the full speech recognition flow: encoding local audio as Base64, getting the full JSON result, attaching timestamps or subtitles, and streaming with SSE.

This tutorial centres on POST /api/v1/audio/transcriptions. The speech endpoint consumes Base64-encoded audio bytes and returns plain text plus optional timestamp data; it also supports SSE for incremental output.

Step 1: Prepare the audio payload

Read the audio into memory and Base64-encode it. The data:audio/...;base64, URI prefix must be stripped — the endpoint only accepts the raw Base64 payload. The server detects the format from the actual audio content, so the filename extension is irrelevant, but the bytes must be one of the supported formats.

  • Supported formats: MP3, WAV, M4A, FLAC, OGG, WEBM.
  • The JSON (Base64) path caps raw audio at 1 MB; exceeding returns 413 audio_too_long.
  • For audio over 1 MB, upload the raw file via multipart/form-data instead (≤ 15 MB): the `params` JSON field (this tutorial's parameters minus audio_data) must precede the `audio` file field. See the ASR endpoint in the API reference for the full field list and error codes.
  • Format detection failure returns 422 unsupported_audio_format; decode failure returns 422 asr_audio_decode_error.

Convert a local audio file to a single-line Base64 string. base64 -w 0 prevents line wrapping.

bash
# Encode a local audio file (no line wrapping)
AUDIO_B64=$(base64 -w 0 ./meeting.mp3)
echo "$AUDIO_B64" | head -c 80
# Output (truncated): SUQzBAAAAAACSlBNTAAA...

Step 2: Send a JSON-mode request

By default stream=false waits for transcription to finish and returns the full result in one response. language accepts a language code or "auto" to let the model detect; punctuation defaults to true, auto-completing punctuation in the transcript.

JSON-mode curl: use jq to inject the Base64 string so the shell does not mangle special characters.

bash
curl -X POST "https://abemono.abestar.com.tw/api/v1/audio/transcriptions" \
  -H "X-API-Key: $ABESTAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg a "$AUDIO_B64" '{
    audio_data: $a,
    language: "zh-TW",
    timestamp_format: "json",
    diarization: true
  }')"

Successful response example (HTTP 200). When timestamp_format="json", segments is populated; switching to srt / webvtt populates the timestamps string instead.

json
{
  "text": "今天的會議改到下午三點。",
  "detected_language": "zh-TW",
  "segments": [
    {
      "id": 0,
      "start": 0.0,
      "end": 2.4,
      "text": "今天的會議改到下午三點。",
      "speaker": "A"
    }
  ],
  "timestamps": null,
  "usage": {
    "quota_percent": {
      "used": 35
    }
  },
  "forced_replacement_count": 0,
  "vocabulary_used": false
}

Optional feature parameters

All parameters below are optional and can be combined freely with either JSON or SSE mode; omitted fields fall back to their defaults. The first seven rows are shared with the text translation endpoint; the last three are speech-only.

The first seven rows are shared with the translation endpoint; the last three are speech-only.
ParameterTypeDefaultDescription
modelstring"tranc-std-v1"Transcription model code. Use GET /audio/models to list what is currently available.
domainstring"general"Domain hint such as "medical", "legal", or "finance", helping with domain terminology.
context{ speech: string }[][]Scene background or prior dialogue, provided in chronological order.
harm_content_filterbooleanfalseEnables harmful-content filtering. Blocked audio returns 400 asr_content_policy_violation.
punctuationbooleantrueAuto-completes punctuation in the transcript. Defaults to true — the opposite of the text translation endpoint's false.
number_format"spoken" | "arabic""spoken"Number transcription: spoken writes numbers out in words, arabic uses digits (1234).
date_format"natural" | "yyyy-MM-dd" | "yyyy/MM/dd" | "MM/dd/yyyy" | "dd-MM-yyyy""natural"Date presentation: natural follows the language's convention; the rest are fixed templates.
timestamp_format"json" | "srt" | "webvtt"omitted → no timestampsSpeech-only. "json" returns structured segments; "srt" / "webvtt" return a ready-to-use subtitle string.
diarizationbooleanfalseSpeech-only. When true, segments carry alphabetic speaker labels ("A" / "B" / "C"…) for multi-speaker meetings.
processing_mode"fast" | "balanced""balanced"Speech-only. fast is quicker; balanced is more accurate. Keep balanced for long audio or accuracy-critical work.

punctuation defaults differ between the two endpoints

Speech recognition defaults punctuation to true (punctuation is added automatically), whereas text translation defaults it to false. To receive a raw transcript without punctuation, send punctuation: false explicitly.

With dictionaries

The speech endpoint accepts the same two dictionary kinds as text translation: vocabulary is soft guidance — matching terms are offered to the model as preferred wording, which suits names, product names, and jargon — while forced_replacement performs string substitutions on the transcript after recognition finishes. Both are referenced by UUID via vocabulary_dictionary_id / forced_replacement_dictionary_id.

Both dictionaries can be attached in JSON mode. Note that language must be an explicit code — auto is not allowed here.

bash
curl -X POST "https://abemono.abestar.com.tw/api/v1/audio/transcriptions" \
  -H "X-API-Key: $ABESTAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg a "$AUDIO_B64" '{
    audio_data: $a,
    language: "zh-TW",
    vocabulary_dictionary_id: "f3c1e9a2-9c2b-4f7a-9d3a-7e2b8a1c4d5e",
    forced_replacement_dictionary_id: "1d2e3f4a-5b6c-7d8e-9f0a-1b2c3d4e5f6a"
  }')"

vocabulary_used and forced_replacement_count in the response let you confirm the dictionaries actually took effect — this pair is speech-only; text translation returns just the latter.

json
{
  "text": "請把這份合約交給法務組長確認。",
  "detected_language": "zh-TW",
  "segments": null,
  "timestamps": null,
  "usage": {
    "quota_percent": {
      "used": 35
    }
  },
  "forced_replacement_count": 2,
  "vocabulary_used": true
}

A vocabulary dictionary cannot be combined with language: "auto"

Vocabulary guidance needs to know the audio language before it can apply the right terms, so sending language: "auto" together with vocabulary_dictionary_id returns 400 validation_error. Always specify an explicit language when attaching a vocabulary dictionary. This restriction is speech-only — the text translation endpoint has no equivalent. As with translation, stream: true also cannot carry forced_replacement_dictionary_id.

Creating dictionaries and further details

See “Dictionary operations” and “Dictionary concepts” for creation, batch creation, full CRUD, and guidance on choosing between the two kinds.

SSE mode: streaming output

Set stream=true when you need live subtitles or progressive display. The response switches to text/event-stream and events flow as head (detected_language) → chunk* (text fragments, zero or more) → tail (optionally carrying segments) → meta (usage / vocabulary_used / forced_replacement_count). On failure, an error event replaces remaining events and closes the stream.

curl example in SSE mode. -N disables curl buffering so events stream out immediately.

bash
curl -N -X POST "https://abemono.abestar.com.tw/api/v1/audio/transcriptions" \
  -H "X-API-Key: $ABESTAR_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --arg a "$AUDIO_B64" '{
    audio_data: $a,
    language: "auto",
    stream: true
  }')"

SSE cannot be combined with forced_replacement

Post-processing substitutions need the full transcript before they can run, which conflicts with the chunk-by-chunk nature of streaming. Sending stream=true together with forced_replacement_dictionary_id returns 400 validation_error. In SSE mode the meta event's forced_replacement_count is always 0. For streaming use cases, fall back to JSON mode or apply substitutions on the client side.

Differences from translation's SSE

Speech recognition's head event carries detected_language (translation uses source_lang); the tail event may carry segments; the meta event includes vocabulary_used and forced_replacement_count alongside usage; error codes always start with asr_ (asr_failed, asr_timeout, asr_content_policy_violation).