Speech-to-text
POST /v1/audio/transcriptions
Transcribes audio into text. Compatible with the OpenAI Audio Transcriptions API — the same endpoint serves OpenAI (Whisper / GPT-4o-transcribe), Google Cloud Chirp, BytePlus Seed ASR, and Alibaba Qwen3-ASR models, and the gateway routes to the right upstream by model ID. Accepts either a multipart/form-data upload (OpenAI SDK compatible) or an application/json body with base64-encoded audio.
Request Body
| Parameter | Type | Description |
|---|---|---|
model* | string | Transcription model ID (e.g. whisper-1, gpt-4o-transcribe, chirp-3, seed-asr-bigmodel, fun-asr, qwen3-asr-flash). See Supported models below. |
file* | file | multipart only — the audio file to transcribe. Required unless input_audio is supplied (JSON). |
input_audio* | object | JSON only — { "data": "<base64>", "format": "mp3" }, or { "url": "<fetchable URL>" } for offline models (seed-asr-bigmodel). Use instead of file for application/json requests. |
response_format | string | Output format: json (default; returns {text, usage}), text (raw transcript string), verbose_json (segments with timestamps), diarized_json (speaker-labeled segments — speaker diarization). Allowed values depend on the model; see Response formats below. |
language | string | Optional ISO-639-1 hint (e.g. "en") to improve accuracy and latency. |
prompt | string | Optional text to guide the transcription style / spelling of proper nouns. |
temperature | number | Sampling temperature 0–1. Default: 0. |
stream | boolean | If true, return partial transcript SSE events. Supported only on gpt-4o-transcribe / gpt-4o-mini-transcribe. The other models (whisper-1, chirp-2, chirp-3, seed-asr-bigmodel, qwen3-asr-flash) do NOT support streaming and return 400 if stream=true. Default: false. |
provider | object | Provider pass-through config. See Provider pass-through below. |
Supported models
Not sure which model? Pick by what you need
| You need | Turn it on with | Recommended models |
|---|---|---|
| Chinese / cantonese, low cost | — | seed-asr-bigmodel fun-asr |
| English, high quality | — | gpt-4o-transcribe chirp-3 |
| Speaker diarization — who said what | response_format=diarized_json | seed-asr-bigmodel chirp-3 gpt-4o-transcribe-diarize |
| Word-level timestamps | response_format=verbose_json | whisper-1 |
| Real-time captions (as you speak) | stream=true | gpt-4o-transcribe |
| Long recordings / meetings | pass a public URL in input_audio.url | seed-asr-bigmodel fun-asr |
| Model | Provider · billing | Input | Diarization | Stream | Best for |
|---|---|---|---|---|---|
whisper-1 | OpenAI · $0.006/min | file | — | — | Word-level timestamps (verbose_json); language must be ISO-639-1 |
gpt-4o-transcribe | OpenAI · token | file | — | ✓ | English, high accuracy; supports streaming |
gpt-4o-mini-transcribe | OpenAI · token | file | — | ✓ | Cheaper multilingual; supports streaming |
gpt-4o-transcribe-diarize | OpenAI · token | file | ✓ | — | English diarization; optional known_speaker_names |
chirp-3 | Google · $0.016/min | file | ✓ | — | High quality; single call ≤60 s |
chirp-2 | Google · $0.016/min | file | — | — | Multilingual USM; use chirp-3 for diarization |
seed-asr-bigmodel | BytePlus · $0.002/min | URL / file | ✓ | — | Chinese, long recordings; cheapest |
fun-asr | Alibaba · $0.0021/min | URL / file | ✓ | — | Chinese · cantonese · dialects; long recordings |
fun-asr-mtl | Alibaba · $0.0021/min | URL / file | ✓ | — | Multilingual + diarization |
fun-asr-flash | Alibaba · $0.0021/min | file | — | — | Fast multilingual; ≤10 MB, sync |
qwen3-asr-flash | Alibaba · $0.0021/min | file | — | — | Multilingual; ≤5 min/call, exact billing |
Diarization = set response_format=diarized_json to get segments[].speaker. Default upload cap 25 MB (see Audio formats).
Availability depends on the channels enabled for your workspace — call /v1/models to see what you can use.
Common pitfalls. (1) Streaming (stream=true) works only on gpt-4o-transcribe / gpt-4o-mini-transcribe — every other model returns 400. (2) input_audio.url (URL input) is supported only by seed-asr-bigmodel; all other models require a file or base64 upload. (3) The language parameter on the OpenAI models (whisper-1, gpt-4o-*) must be an ISO-639-1 code such as en or zh — locale codes like yue-CN are rejected; Chirp / Seed ASR / Qwen accept locale codes, or omit language to auto-detect. (4) If audio exceeds a model's per-request limit, split it into chunks — or use seed-asr-bigmodel (via URL) for long recordings (~20 min/request). (5) Silent / no-speech audio returns 200 with an empty string {"text":""}, not an error.
Supported audio formats
mp3wavoggflacm4a
For multipart uploads the format is inferred from the filename extension. For base64 (JSON) requests, set input_audio.format explicitly. Max upload size: 25 MB.
Response formats
The response_format values accepted depend on the model. json is the default for all models (returns { text, usage }); text returns the raw transcript string.
whisper-1—json,text,verbose_jsongpt-4o-transcribe,gpt-4o-mini-transcribe—json,textchirp-3,chirp-2,seed-asr-bigmodel,qwen3-asr-flash,fun-asr-flash—json,textgpt-4o-transcribe-diarize,chirp-3,seed-asr-bigmodel,fun-asr,fun-asr-mtl— alsodiarized_json(speaker-labeled segments; see below)
verbose_json (segment timestamps) is whisper-1 only. Requesting an unsupported format returns a 400.
Speaker diarization
Set response_format=diarized_json to get speaker-labeled segments — the response adds a segments array where each entry carries a speaker label plus start/end timestamps (seconds). Supported on gpt-4o-transcribe-diarize (OpenAI), chirp-3 (Google), seed-asr-bigmodel (BytePlus — best value for Chinese and long recordings), and fun-asr / fun-asr-mtl (Alibaba — offline recording-file, accepts a URL or file). Other models ignore diarized_json and return plain text; chirp-2 rejects it (use chirp-3).
Speaker labels are provider-native (e.g. "0"/"1" for chirp-3 & seed-asr, "A"/"B" for gpt-4o-transcribe-diarize); they identify distinct speakers but are not stable real-world identities. On gpt-4o-transcribe-diarize you may pass known_speaker_names to bias labeling.
# Chinese meeting, cheapest diarization (BytePlus seed-asr):
curl https://synthorai.io/v1/audio/transcriptions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seed-asr-bigmodel",
"input_audio": { "url": "https://your-bucket/signed/meeting.wav", "language": "zh" },
"response_format": "diarized_json"
}'
# English / highest quality (Google chirp-3, <=60 s per request):
curl https://synthorai.io/v1/audio/transcriptions \
-H "Authorization: Bearer YOUR_API_KEY" \
-F model=chirp-3 \
-F file=@call.wav \
-F response_format=diarized_json Example Response
{
"task": "transcribe",
"text": "喂 你好 我这边是客服 有什么可以帮您",
"duration": 6.2,
"segments": [
{ "id": 0, "start": 0.10, "end": 0.90, "speaker": "0", "text": "喂 你好" },
{ "id": 1, "start": 1.20, "end": 6.20, "speaker": "1", "text": "我这边是客服 有什么可以帮您" }
]
} Example Request (multipart)
curl https://synthorai.io/v1/audio/transcriptions \
-H "Authorization: Bearer YOUR_API_KEY" \
-F model=gpt-4o-transcribe \
-F file=@meeting.mp3 \
-F response_format=json \
-F language=en Example Request (base64 JSON)
POST /v1/audio/transcriptions
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"model": "gpt-4o-transcribe",
"input_audio": {
"data": "UklGRiQAAABXQVZFZm10I...",
"format": "wav"
},
"response_format": "json",
"language": "en"
} Example — Offline ASR (seed-asr-bigmodel)
seed-asr-bigmodel (BytePlus overseas offline ASR) accepts two mutually-exclusive inputs: a fetchable input_audio.url (recommended — your own time-limited signed object-storage URL; the audio never touches our storage), or a direct file/byte upload (we stage it in a private bucket and delete it right after transcription). Limitations: no streaming (stream=true returns 400); file uploads up to 25 MB (use a URL for larger recordings); single-request processing is capped at ~20 minutes, so split very long audio. Billed by audio duration ($0.002/min).
# ① Quick test — replace ONLY YOUR_API_KEY and run it (uses our hosted sample audio).
curl https://synthorai.io/v1/audio/transcriptions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seed-asr-bigmodel",
"input_audio": {
"url": "https://synthorai-asr-sample-360831509259.s3.us-west-1.amazonaws.com/seed-asr-sample.wav",
"language": "zh"
}
}'
# → {"text":"..."}
# ② Your own audio file (<=25 MB) — replace key + file path.
curl https://synthorai.io/v1/audio/transcriptions \
-H "Authorization: Bearer YOUR_API_KEY" \
-F model=seed-asr-bigmodel \
-F language=zh \
-F file=@/path/to/your-audio.wav
# ③ Your own signed URL (recommended for large files / privacy) — replace key + URL.
curl https://synthorai.io/v1/audio/transcriptions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"seed-asr-bigmodel","input_audio":{"url":"https://your-bucket/signed/meeting.wav","language":"zh"}}' Notes / gotchas: (1) language is optional — common values are zh (Mandarin), yue-CN (Cantonese), en-US; omit it to auto-detect. (2) When using input_audio.url, the URL must be publicly fetchable by the transcription service — a private/internal/behind-auth URL will fail; a time-limited signed (pre-signed) object-storage URL is the recommended way. (3) File upload and URL are mutually exclusive; pick one. (4) Audio with no recognizable speech (silence) returns 200 with an empty string {"text":""}, not an error.
Example — OpenAI Python SDK
The endpoint is OpenAI-compatible, so the official OpenAI SDK works directly by overriding base_url. Works the same for whisper-1, gpt-4o-transcribe, chirp-3, seed-asr-bigmodel and qwen3-asr-flash — just change model.
from openai import OpenAI
client = OpenAI(
base_url="https://synthorai.io/v1",
api_key="YOUR_API_KEY",
)
with open("meeting.mp3", "rb") as f:
result = client.audio.transcriptions.create(
model="gpt-4o-transcribe",
file=f,
language="en",
# prompt="Synthorai, BYOK, transcribe", # optional vocabulary hint
response_format="json",
)
print(result.text)
# Token-level usage on gpt-4o-*:
if getattr(result, "usage", None):
print(result.usage) Example — OpenAI Node.js SDK
import OpenAI from "openai";
import fs from "node:fs";
const client = new OpenAI({
baseURL: "https://synthorai.io/v1",
apiKey: process.env.SYNTHORAI_API_KEY,
});
const result = await client.audio.transcriptions.create({
model: "gpt-4o-transcribe",
file: fs.createReadStream("meeting.mp3"),
language: "en",
response_format: "json",
});
console.log(result.text); Streaming (SSE)
Set stream=true on gpt-4o-transcribe, gpt-4o-mini-transcribe to receive incremental events as the audio is decoded. The response Content-Type is text/event-stream and events end with data: [DONE]. whisper-1 does not support streaming (returns 400 if stream=true).
Event types you'll see for gpt-4o-transcribe:
data: {"type":"transcript.text.delta","delta":"Hello "}
data: {"type":"transcript.text.delta","delta":"world."}
data: {"type":"transcript.text.done","text":"Hello world."}
data: {"type":"usage","usage":{"type":"tokens","input_tokens":689,"output_tokens":287,"total_tokens":976,"input_token_details":{"audio_tokens":689,"text_tokens":0,"cached_tokens":0}}}
data: [DONE] The gateway emits a canonical usage object documented under Example Response. The final usage event always carries the full input_token_details breakdown for accurate audio-rate billing.
Errors
All errors follow the OpenAI envelope: { error: { message, type, code, param? } }. Common cases:
400 model_not_found— model lacks transcription capability in this workspace's channels.400 byok_strict_no_key— workspace is strict BYOK (byok_fallback_to_pool=false) and has no vault key for the resolved provider.400 content_policy_violation— thepromptfield tripped a guardrail rule (e.g. BYOK key leak).402 quota_exceeded— estimated cost exceeds workspace remaining quota. BYOK is exempted.408— the request body did not finish uploading within the server read timeout. Common with large audio over a slow connection; retry or send a smaller file.413— audio exceeds the 25 MB cap.502 upstream_error— upstream network failure or non-2xx response from the model provider.
Example Response
{
"text": "Thanks for joining today's call. Let's get started.",
"usage": {
"type": "tokens",
"input_tokens": 689,
"output_tokens": 287,
"total_tokens": 976,
"input_token_details": {
"audio_tokens": 689,
"text_tokens": 0,
"cached_tokens": 0
}
}
} Uniform usage object. Token-billed transcription models (gpt-4o-transcribe / -mini) return a uniform usage object — all fields always present, zero-filled when a sub-count is not reported. The OpenAI models report a native audio/text input split, so for pure-audio transcription input_token_details.audio_tokens == input_tokens typically holds. cached_tokens is the portion of input served from a provider-side cache.
whisper-1 is the one exception: it is billed by audio duration, not tokens, so its response carries no token usage object. With response_format=text the response body is the raw transcript string instead of a JSON object.