whisper-large-v3-turbo is accessible via the same /v1/audio/transcriptions endpoint as the OpenAI Whisper API, so any code written for OpenAI works as a drop-in replacement by changing base_url. See the Whisper model page for supported formats, language codes, and inference parameters.
For speaker separation, word timestamps, subtitles, or recordings longer than 30 minutes, see Speech-to-text with Cohere Transcribe.
Setup
user@local $ pip install openai
user@local $ export OPENAI_API_KEY="sk-…"
For chunking long or large files (over 30 minutes or over 25 MB):
user@local $ pip install pydub
# pydub requires ffmpeg
user@local $ brew install ffmpeg # macOS
user@local $ apt-get install ffmpeg # Debian/Ubuntu
Basic transcription
import os
from openai import OpenAI
client = OpenAI(base_url="https://llm.aihosting.mittwald.de/v1")
with open("recording.mp3", "rb") as f:
result = client.audio.transcriptions.create(
model="whisper-large-v3-turbo",
file=f,
language="en", # Always set explicitly — default is "de"
response_format="json",
temperature=0,
)
print(result.text)
Multiple languages
files_and_languages = [
("meeting_de.mp3", "de"),
("interview_fr.wav", "fr"),
("podcast_en.ogg", "en"),
("lecture_ja.flac", "ja"),
]
for filename, lang in files_and_languages:
with open(filename, "rb") as f:
result = client.audio.transcriptions.create(
model="whisper-large-v3-turbo",
file=f,
language=lang,
response_format="json",
temperature=0,
)
print(f"[{lang}] {result.text[:120]}")
Long or large files (> 30 minutes or > 25 MB)
The API accepts files up to 25 MB and up to 30 minutes of audio. Both limits apply independently. Which one you hit first depends on the bitrate: 25 MB hold about 52 minutes of 64 kbit/s mono mp3, but only about 27 minutes of 128 kbit/s stereo. The example below splits longer recordings at silence points with pydub, packs the pieces back into chunks of up to 25 minutes, and re-encodes each chunk as mono mp3, so every request stays below both limits and no words are cut mid-utterance:
from io import BytesIO
from pydub import AudioSegment
from pydub.silence import split_on_silence
MAX_CHUNK_MS = 25 * 60 * 1000 # Stay below the 30-minute duration limit
def transcribe_large_file(path: str, language: str = "en") -> str:
"""Transcribe a file of any size or length by splitting at silence points."""
audio = AudioSegment.from_file(path)
# Split at pauses longer than 700 ms with silence below -40 dBFS
pieces = split_on_silence(
audio,
min_silence_len=700,
silence_thresh=-40,
keep_silence=300, # Keep 300 ms of silence at each edge for context
seek_step=10, # Check every 10 ms instead of every 1 ms, about 10x faster
) or [audio]
# A passage without pauses can still exceed the limit: cut it hard
bounded: list[AudioSegment] = []
for piece in pieces:
bounded.extend(piece[i: i + MAX_CHUNK_MS] for i in range(0, len(piece), MAX_CHUNK_MS))
# Pack consecutive pieces into as few requests as possible
chunks: list[AudioSegment] = []
for piece in bounded:
if chunks and len(chunks[-1]) + len(piece) <= MAX_CHUNK_MS:
chunks[-1] += piece
else:
chunks.append(piece)
transcripts: list[str] = []
for chunk in chunks:
buf = BytesIO()
# Mono at 64 kbit/s: 25 minutes come to about 12 MB, well below 25 MB
chunk.set_channels(1).export(buf, format="mp3", bitrate="64k")
buf.seek(0)
buf.name = "chunk.mp3" # openai SDK reads the .name attribute for MIME type
result = client.audio.transcriptions.create(
model="whisper-large-v3-turbo",
file=buf,
language=language,
response_format="json",
temperature=0,
)
transcripts.append(result.text)
return " ".join(transcripts)
full_text = transcribe_large_file("long_interview.mp3", language="de")
print(full_text)
Transcription + summarisation pipeline
Chain Whisper with a chat model to go directly from audio to a written summary:
def transcribe_and_summarise(audio_path: str, language: str = "en") -> dict:
# Step 1: transcribe
with open(audio_path, "rb") as f:
result = client.audio.transcriptions.create(
model="whisper-large-v3-turbo",
file=f,
language=language,
response_format="json",
temperature=0,
)
transcript = result.text
# Step 2: summarise
summary_resp = client.chat.completions.create(
model="Qwen3.6-35B-A3B-FP8",
messages=[
{
"role": "system",
"content": "Summarise the transcript concisely. Use bullet points for key topics.",
},
{"role": "user", "content": transcript},
],
temperature=0.7,
max_tokens=512,
extra_body={"chat_template_kwargs": {"enable_thinking": False}},
)
return {
"transcript": transcript,
"summary": summary_resp.choices[0].message.content,
}
output = transcribe_and_summarise("team_standup.mp3", language="en")
print("Transcript:\n", output["transcript"])
print("\nSummary:\n", output["summary"])
Drop-in replacement for OpenAI
Existing code written for the OpenAI Whisper API requires only a base_url change:
# Before (OpenAI)
client = OpenAI()
# After (mittwald AI Hosting)
client = OpenAI(base_url="https://llm.aihosting.mittwald.de/v1")
All other code — file handling, response_format, language, temperature — stays identical.
Timestamps and language detection with verbose_json
response_format="verbose_json" returns per-segment timestamps, detected language, and total duration:
with open("recording.mp3", "rb") as f:
result = client.audio.transcriptions.create(
model="whisper-large-v3-turbo",
file=f,
language="en",
response_format="verbose_json",
temperature=0,
)
print(result.text)
print(f"Language: {result.language}")
print(f"Duration: {result.duration}s")
for seg in (result.segments or []):
print(f" [{seg.start:.1f}s–{seg.end:.1f}s] {seg.text}")