Whisper Large V3 CT2: Transcription That Knows Who Said What
There’s a new transcription model in the deAPI catalogue: WhisperLargeV3Ct2. It runs the Whisper Large V3 weights you already know on the CTranslate2 runtime, so the transcript text is what you’d expect from Whisper.
What changed is the shape of the output. It’s JSON now, with per-word timings and speaker labels.
Speaker labels
Transcribe a two-person interview and Whisper hands you one paragraph with both speakers in it.
Turn on diarize and each part of the transcript comes back tagged: SPEAKER_00, SPEAKER_01, and so on. The labels are anonymous and assigned per job, so SPEAKER_00 in one run has nothing to do with SPEAKER_00 in the next. Map them to real names once on your side and you get the turn-by-turn layout people expect from an interview.
Pair diarize with ts_level: word. Diarization switches timestamps on by default, but at segment level, and a segment can be long enough to swallow a speaker change. When that happens the whole span gets a single label, and the output looks perfectly well-formed while being wrong.
Word-level timestamps
Set ts_level: word and every word comes back with its own start and end. That covers the work where “roughly around here” isn’t good enough:
- subtitles that break where a human would break them
- karaoke and lyric sync
- cutting clips on exact word boundaries
- dubbing and lip-sync alignment
- transcript search that jumps straight to the moment
Segment timestamps are included in the base price. Word level and diarize both carry a 1.5× multiplier on the duration-based price, and diarize carries it at any granularity.
JSON output
CT2 returns JSON on every request, even with timestamps off:
{
"text": "Alright, so here we are in front of the elephants...",
"language": "en",
"language_probability": 0.9824,
"ts_level": "word",
"diarization_available": true,
"segments": [
{
"start": 0,
"end": 19.01,
"text": "Alright, so here we are in front of the elephants...",
"avg_logprob": -0.1587,
"speaker": "SPEAKER_00",
"words": [
{ "word": "Alright,", "start": 0, "end": 1.46, "score": -9.0664, "speaker": "SPEAKER_00" },
{ "word": "so", "start": 1.52, "end": 1.6, "score": -4.7598, "speaker": "SPEAKER_00" }
]
}
]
}
You get the detected language with a probability, plus segments, words, speakers and a confidence score on each. Read fields instead of pulling timestamps out of a string with a regex.
Those confidence scores pay off on long recordings. Sort segments by avg_logprob ascending and the shakiest passages float to the top, which beats listening to a two-hour file to find the one sentence Whisper misheard.
Getting started
The slug is WhisperLargeV3Ct2. One endpoint handles both audio and video, from a URL or a file upload:
curl -X POST "<https://api.deapi.ai/api/v2/audio/transcriptions>" \
-H "Authorization: Bearer $DEAPI_API_KEY" \
-H "Accept: application/json" \
-F "source_url=https://www.youtube.com/watch?v=VIDEO_ID" \
-F "model=WhisperLargeV3Ct2" \
-F "include_ts=true" \
-F "ts_level=word" \
-F "diarize=true"
You get a request_id back, then poll for the result. source_url takes YouTube, X, Twitch, Kick, TikTok and X Spaces, or swap it for source_file to upload your own. TikTok runs on this model only.
One thing worth knowing before you scale up: a source with no speech comes back with an empty transcript and status: "done", not an error. Music-only clips do this constantly, so branch on it rather than logging a failure.
If you already transcribe on deAPI, see How to Transcribe YouTube Videos with AI for the basics.
Paste a podcast link and see what comes back. New accounts get $5 in credits, no card required. Get your API key →