Transcription
Turn audio into text with a shared TranscriptionModel API.
Every transcription provider conforms to TranscriptionModel and works with
transcribe.
let result = try await transcribe(
model: OpenAITranscriptionModel("whisper-1"),
audio: audioData,
mediaType: "audio/mpeg"
)
print(result.text)
for segment in result.segments {
print("\(segment.startSecond)s–\(segment.endSecond)s", segment.text)
}Results include the text plus segments, detected language, and duration when
the provider returns them. When mediaType is generic
(application/octet-stream, audio/*, or empty), the container is sniffed
from the bytes — MP4/M4A from its ftyp box, plus WAV, Ogg, FLAC, and MP3 —
so a mislabeled upload still reaches the provider correctly.
Streaming transcription
streamTranscribe transcribes live audio, emitting updates before the audio
is complete. Models opt in by conforming to StreamingTranscriptionModel;
DeepgramTranscriptionModel does, over Deepgram's live WebSocket API.
let result = try streamTranscribe(
model: DeepgramTranscriptionModel("nova-3"),
audio: microphoneChunks, // AsyncThrowingStream<Data, Error>
mediaType: "audio/pcm"
)
for try await part in result.fullStream {
switch part {
case .partialTranscript(let text): draft = text // replaces
case .transcriptDelta(let text): transcript += text // appends
case .speechStart, .speechEnd, .segment, .language: break
case .finish(let response): print(response.text)
}
}Interim results arrive as .partialTranscript (each one replaces the last)
and finalized text as .transcriptDelta (each one appends), so textStream
and the text promise never double-count a revised phrase.
fullStream is single-consumer with no replay: read it once, or skip it and
await result.text / .segments / .language, which drain the stream
internally and cache. Cancelling the result — or a failure before streaming
starts, such as a bad key — cancels the audio stream you passed in, so an
upstream producer never hangs.
Native request fields go in providerOptions:
let result = try await transcribe(
model: OpenAITranscriptionModel("whisper-1"),
audio: audioData,
mediaType: "audio/wav",
providerOptions: ["openai": ["language": "en", "temperature": 0]]
)AssemblyAI, Rev.ai, and Gladia are asynchronous upstream. Their model types
submit, poll, and fetch internally, so your call remains a single await.
Models
| Provider | Model type | Default or example model | Key |
|---|---|---|---|
| OpenAI | OpenAITranscriptionModel | whisper-1 | OPENAI_API_KEY |
| ElevenLabs | ElevenLabsTranscriptionModel | scribe_v2 | ELEVENLABS_API_KEY |
| Deepgram | DeepgramTranscriptionModel | nova-3 | DEEPGRAM_API_KEY |
| AssemblyAI | AssemblyAITranscriptionModel | universal-3-5-pro | ASSEMBLYAI_API_KEY |
| Rev.ai | RevAITranscriptionModel | machine | REVAI_API_KEY |
| Gladia | GladiaTranscriptionModel | solaria-1 | GLADIA_API_KEY |
| Sarvam | SarvamTranscriptionModel | saaras:v3 | SARVAM_API_KEY |
Groq-hosted Whisper also works through OpenAITranscriptionModel with Groq's
base URL; see the Groq provider page.