🎁 New Sign up free, 10 calls on us. Up to $1, no card needed.

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

ParameterTypeDescription
model*stringTranscription model ID (e.g. whisper-1, gpt-4o-transcribe, chirp-3, seed-asr-bigmodel, fun-asr, qwen3-asr-flash). See Supported models below.
file*filemultipart only — the audio file to transcribe. Required unless input_audio is supplied (JSON).
input_audio*objectJSON only — { "data": "<base64>", "format": "mp3" }, or { "url": "<fetchable URL>" } for offline models (seed-asr-bigmodel). Use instead of file for application/json requests.
response_formatstringOutput 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.
languagestringOptional ISO-639-1 hint (e.g. "en") to improve accuracy and latency.
promptstringOptional text to guide the transcription style / spelling of proper nouns.
temperaturenumberSampling temperature 0–1. Default: 0.
streambooleanIf 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.
providerobjectProvider pass-through config. See Provider pass-through below.

Supported models

Not sure which model? Pick by what you need

You needTurn it on withRecommended 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
ModelProvider · billingInputDiarizationStreamBest 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

  • mp3
  • wav
  • ogg
  • flac
  • m4a

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-1json, text, verbose_json
  • gpt-4o-transcribe, gpt-4o-mini-transcribejson, text
  • chirp-3, chirp-2, seed-asr-bigmodel, qwen3-asr-flash, fun-asr-flashjson, text
  • gpt-4o-transcribe-diarize, chirp-3, seed-asr-bigmodel, fun-asr, fun-asr-mtl — also diarized_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 — the prompt field 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.