Both of these APIs will transcribe a clinical encounter competently. That's not the interesting part. The interesting part is what happens to the fifteen or twenty words per encounter that carry clinical meaning: the drug names, the dosages, the procedure references. Those are the words that end up in a note, feed a summarizer, and occasionally cause someone to chart the wrong medication.
On the medical benchmark published on the medical transcription use case page, Universal-3.5 Pro with Medical Mode records a 3.2% Missed Entity Rate (MER). Deepgram Nova-3 Medical records 4.7% on the same table. The gap looks small until you multiply it by the clinical entities in a fifteen-minute visit, and then by the visits a health system runs in a day.
This is a head-to-head on the dimensions that differ: entity accuracy, how each system gets to it, context handling, diarization for multi-party consultations, streaming, and what migration looks like. Every figure here is published and reproducible on your own audio.
The short version
| Dimension | AssemblyAI | Deepgram |
|---|---|---|
| Medical option | Medical Mode: domain: "medical-v1" on Universal-3.5 Pro and Universal-2 (pre-recorded) and Universal-3.6 Pro Realtime, Universal-3.5 Pro Realtime and Universal-Streaming (streaming) |
Nova-3 Medical: a separate model you route to |
| How it gets there | LLM post-processing pass that corrects medical terms after transcription | Dedicated medical model |
| Medical Missed Entity Rate (use-case table) | 3.2% | 4.7% |
| Medical WER (same table) | 5.3% | 6.1% |
| Drug-name miss rate, batch (Medical Mode research, 9 sets, 4 languages) | 10.7% | 26.6% |
| Drug-name miss rate and p90 time-to-final, streaming (same research) | 13.0%, 1.28 s (0.63 s without Medical Mode) | 29.4%, 0.87 s |
| Terminology input | Keyterms plus free-text context prompting | Keyterm prompting (list-based) |
| Diarization, cpWER | 26.7 | 34.8 |
| Medical languages | English, Spanish, German, French (pre-recorded and streaming) | Pins English only for pre-recorded (English and German for streaming); other languages ran unpinned in the Medical Mode research |
| Dictation | Dictation API (no Medical Mode) | dictation parameter for spoken punctuation, English only |
| BAA | BAA available; paid accounts sign self-serve | Enterprise customers |
Missed entity rate is the number to compare on
If you're benchmarking these two on word error rate, stop. WER treats every token as equal weight, so "the" and "amiodarone" count the same. A model can post a strong overall WER while concentrating its errors in the medication names, which is the failure that matters and the one WER is structurally blind to. The MER vs WER explainer goes deeper on why.
Missed Entity Rate scores only the clinically meaningful tokens: drugs, dosages, conditions, procedures, anatomy. On the use-case table, the WER gap between the two is 0.8 points (5.3% vs 6.1%). The MER gap is 1.5 points on a much smaller base: 3.2% vs 4.7%, which means Nova-3 Medical misses roughly 47% more medical entities.
Against the base model without it, Medical Mode gives roughly 20% fewer missed medical entities and about 87% fewer medical entity errors.
Run your own eval anyway
Published benchmarks tell you which models are in contention; your audio tells you which one wins. The exercise takes two days:
- Pull 25 to 50 real encounters that represent your actual mix: specialties, accents, mic setups, room noise.
- Build a ground-truth entity list from the notes your clinicians wrote for those encounters.
- Run both APIs with their medical options on and score entity capture, not WER.
- Score diarization separately, and score it on short turns specifically.
If an advantage disappears on your audio, that's the most valuable thing you'll learn in the whole procurement cycle.
Drug names head to head: Medical Mode vs nova-3-medical
The use-case table above scores missed entities across every clinical category. AssemblyAI's Medical Mode research measured something narrower: how often each system misses medical terms and drug names. Figures are averages over 9 sets in 4 languages, and neither vendor used custom vocabulary. It's a different metric from MER, so read the two sets of numbers separately.
| System | WER | Medical-term miss | Drug-name miss |
|---|---|---|---|
| Batch: AssemblyAI + Medical Mode | 9.5% | 5.2% | 10.7% |
| Batch: Deepgram nova-3-medical | 11.2% | 9.7% | 26.6% |
| Streaming: AssemblyAI + Medical Mode | 10.4% | 6.3% | 13.0% |
| Streaming: Deepgram nova-3-medical | 13.2% | 12.4% | 29.4% |
Latency is the trade-off. At p90 time-to-final in streaming, AssemblyAI with Medical Mode finalizes in 1.28 s and Deepgram nova-3-medical in 0.87 s, so Medical Mode finalizes about 0.4 s later. Without Medical Mode, AssemblyAI finalizes in 0.63 s, faster than Deepgram, and still misses fewer drug names (15.7% vs 29.4%).
Deepgram's medical model accepts only English as a pinned language for pre-recorded audio, and English and German for streaming, so it ran unpinned on the other sets.
Medical Mode's gains come mainly on medication names and, to a lesser extent, disease names. It doesn't measurably change anatomy, dosages, tests or procedures, which are mostly numbers, units or everyday words. On English sets, WER moves by 0.1 point or less with it on, so it fixes terms without disturbing the rest of the transcript. The gain is larger in batch than in streaming, where correction runs turn by turn with less context and under a time budget.
What the errors actually look like
Abstract percentages don't convey the risk profile. The concrete version:
- Sound-alike drug pairs. Hydralazine and hydroxyzine. Clonidine and Klonopin. Metoprolol and metronidazole. These are separated by one or two phonemes, and those phonemes are the first casualties when a clinician talks fast over a running sink.
- Dosage mangling. "Twenty-five milligrams BID" becoming "twenty-five milligrams by day," or a decimal shifting. Numbers are where formatting decisions in the model become clinical decisions in the chart.
- Negation loss. "No history of MI" losing the "no." This is the error class that scares clinical safety reviewers most, because it inverts meaning while looking perfectly fluent.
- Attribution flips. A patient's report of a symptom attributed to the clinician, or a family member's speculation recorded as patient history.
The first is a vocabulary problem, and that's where a medical option earns its keep. Dosage errors are numbers and units, which Medical Mode doesn't measurably change, so check them in your own eval. Negation is a model-quality problem. The fourth is a diarization problem, covered below.
Two ways to get to medical accuracy
The architectural difference between the two products is simple to state.
Deepgram ships a separate model. Nova-3 Medical is its own model ID (nova-3-medical) that you route clinical audio to, for pre-recorded and streaming. Anything you've tuned on Deepgram's general models, you re-validate on the medical one.
AssemblyAI ships a parameter. Medical Mode is domain: "medical-v1" on the same flagship models you'd use anyway: Universal-3.5 Pro for pre-recorded audio and Universal-3.6 Pro Realtime for streaming. It's an LLM post-processing pass. The speech model transcribes first; then a correction step reads the transcript, uses the surrounding clinical context, and fixes medical terms the model heard but spelled wrong. It covers English, Spanish, German and French, on both pre-recorded and streaming audio.
Medical Mode now runs on an updated correction model that improves medical-term recall: up to 13% fewer missed medical terms than the base model, with no rise in wrongly inserted terms. Same domain: "medical-v1", no code change.
The limit to know: Medical Mode corrects misspellings. If the speech model writes a wrong but valid word, the correction pass may not catch it. That's why the context lever in the next section matters.
Sign up free, set domain to medical-v1, and score entity capture against whatever you're running now. No commitment, no sales call.
Context handling: free text vs a keyword list
This is the difference with the biggest downstream consequence, and it rarely shows up in a feature comparison.
Deepgram's approach to specialist terminology is keyterm prompting: you pass a list of terms you expect. The cost is operational. Someone has to build, maintain and version those lists, per specialty, per formulary and, if you're a vendor, per customer.
AssemblyAI takes keyterms too, and they work alongside Medical Mode. On long recordings, keep the list to about 200 terms or fewer, since very long lists start adding false insertions. But the bigger lever is contextual prompting, which takes free text: you describe what the model is about to hear, and it adapts. On a public benchmark of 20,000 real voice-agent calls, detailed context cut medical-term entity errors by 43% and scenario-level context by 24%. In internal healthcare testing, feeding a patient's prior-visit note cut missed medical terms by 31%.
The difference between those two designs is the difference between maintaining a dictionary and passing a pointer to data you already have. A patient on lisinopril, metformin and atorvastatin who was seen six weeks ago for the same complaint already has a vocabulary. You don't need to curate it; you need to hand it over.
Diarization for multi-party consultations
Most clinical encounters have more than two participants: a caregiver, an interpreter, a resident, a parent answering for a child. Most speaker diarization benchmarks are scored on diarization error rate, which measures how much audio time landed under the right speaker. That metric rewards getting long monologues right and quietly forgives missing every short interjection.
Short interjections are the clinical content. "Since when?" "About a week." "Any chest pain with it?" "No."
cpWER only improves if the correct words land under the correct speaker. On AssemblyAI's published pre-recorded benchmarks (DiPCo, CallHome, NOTSOFAR and AMI), Universal-3.5 Pro posts cpWER 26.7 and Deepgram Nova-3 34.8. On streaming, AssemblyAI labels arrive with each turn, you can cap a session at up to 10 speakers, and an end-of-session refinement pass sends corrected labels for any turns it reassigns.
If you're evaluating both on diarization, score it on short turns. Take the utterances under five words and check attribution on those alone. That's the number that will differ.
Streaming: Nova-3 Medical, Flux and Universal-3.6 Pro Realtime
Deepgram has two streaming stories, and they're easy to confuse. Nova-3 Medical streams, and it's the one with a medical vocabulary. Flux is Deepgram's conversational streaming model for voice agents, with English and multilingual variants. It isn't a medical model, so a clinical voice agent on Flux runs without Deepgram's medical vocabulary.
On AssemblyAI, the streaming model is Universal-3.6 Pro Realtime, and Medical Mode is the same domain parameter you use for pre-recorded audio. A clinical voice agent, a live scribe view and a post-visit batch job all run on one parameter. For the voice-agent side of this comparison, see AssemblyAI vs Deepgram for voice agents.
Medical dictation
Dictation is where Deepgram's pitch is strongest, so it's worth being precise. Deepgram has a dictation parameter that turns spoken punctuation commands ("comma", "period") into punctuation, English only.
AssemblyAI has a separate Dictation API that runs speech-to-text and an LLM cleanup pass in one call, returning both the verbatim text and a cleaned version. Medical Mode isn't available on the Dictation API. It takes keyterms_prompt for drug names, but the 3.2% MER figure belongs to the pre-recorded and streaming paths, not to dictation. There's no native spoken-punctuation mode either; the documented approach is an LLM pass over each finalized turn to interpret command words.
Multilingual clinical audio
Universal-3.5 Pro code-switches natively across 18 languages for pre-recorded audio, and Universal-3.6 Pro Realtime covers 32 languages for streaming, with no configuration. A patient moving between English and Spanish mid-sentence doesn't need upstream language detection or routing.
Medical Mode covers English, Spanish, German and French. Keep those two facts separate when you evaluate: the base transcription handles considerably more languages than Medical Mode does. If Medical Mode gets a language it doesn't support, the API skips the correction, returns a warning, and doesn't charge for it.
Where each approach breaks
Flat and specific, for both:
- Deepgram Nova-3 Medical misses more medical entities on the use-case table (4.7% vs 3.2% MER). It's a separate model, so medical and general traffic run on different models with different error profiles. Terminology coverage depends on keyterm lists you maintain. Its BAA is for Enterprise customers.
- Deepgram Flux has no medical vocabulary, so clinical voice agents on it give up the medical model.
- AssemblyAI Medical Mode corrects misspellings, so a wrong-but-valid word can slip through, which is why context prompting matters. It covers four languages. It isn't on the Dictation API or the Sync API, and there's no native spoken-punctuation mode. In streaming, it finalizes later than Deepgram: 1.28 s at p90 time-to-final vs 0.87 s for nova-3-medical.
Test both on your own encounters in every case. Two days of eval is cheap against a multi-year contract.
Migrating from Deepgram
Practically, this is a small change. You're swapping a client and a config object, not rebuilding a pipeline. Here's how the parameters map:
| Deepgram | AssemblyAI pre-recorded | AssemblyAI streaming |
|---|---|---|
model=nova-3-medical |
speech_models=["universal-3-5-pro", "universal-2"] plus domain="medical-v1" |
speech_model="universal-3-6-pro" plus domain="medical-v1" |
keyterm |
keyterms_prompt |
keyterms_prompt |
diarize=true |
speaker_labels=True |
speaker_labels=True |
dictation=true |
LLM pass over the transcript | LLM pass over each finalized turn |
Pre-recorded, with Medical Mode, diarization and a short keyterm list:
import os
from assemblyai.prerecorded.v2 import Transcriber, TranscriptionConfig
config = TranscriptionConfig(
speech_models=["universal-3-5-pro", "universal-2"],
domain="medical-v1",
speaker_labels=True,
keyterms_prompt=["metoprolol", "lisinopril", "atorvastatin"],
)
transcriber = Transcriber(api_key=os.environ["ASSEMBLYAI_API_KEY"])
transcript = transcriber.transcribe("encounter.wav", config)
if transcript.status == "error":
raise RuntimeError(transcript.error)
for utterance in transcript.utterances:
print(f"Speaker {utterance.speaker}: {utterance.text}")Streaming connects to wss://streaming.assemblyai.com/v3/ws and takes speech_model (singular). This example replays a 16 kHz mono WAV in 100 ms chunks; in production you'd stream from the microphone.
import os
import time
import wave
from assemblyai.streaming.v3 import (
RealTimeError,
RealTimeEvents,
RealTimeParameters,
RealTimeTranscriber,
RealTimeTranscriberOptions,
SpeakerRevisionEvent,
TurnEvent,
)
def on_turn(client: RealTimeTranscriber, event: TurnEvent):
if event.end_of_turn and event.transcript:
print(f"[{event.speaker_label}] {event.transcript}")
def on_revision(client: RealTimeTranscriber, event: SpeakerRevisionEvent):
# End-of-session pass: corrected speaker labels for earlier turns.
for revision in event.revisions:
print(f"turn {revision.turn_order} -> {revision.speaker_label}")
def on_error(client: RealTimeTranscriber, error: RealTimeError):
print(f"Error: {error}")
client = RealTimeTranscriber(
RealTimeTranscriberOptions(terminate_timeout=30.0),
api_key=os.environ["ASSEMBLYAI_API_KEY"],
)
client.on(RealTimeEvents.Turn, on_turn)
client.on(RealTimeEvents.SpeakerRevision, on_revision)
client.on(RealTimeEvents.Error, on_error)
client.connect(
RealTimeParameters(
sample_rate=16000,
speech_model="universal-3-6-pro",
domain="medical-v1",
speaker_labels=True,
voice_focus="far-field", # room mic; A/B test on multi-party visits
min_turn_silence=800,
max_turn_silence=2000,
)
)
# Replay a 16 kHz, mono, 16-bit WAV in 100 ms chunks, paced like live audio.
with wave.open("encounter.wav", "rb") as wav:
frames_per_chunk = wav.getframerate() // 10
while chunk := wav.readframes(frames_per_chunk):
client.stream(chunk)
time.sleep(0.1)
# Terminate flushes the last turn and waits for the Termination event.
client.disconnect(terminate=True)Register handlers with the RealTimeEvents enum. Passing the TurnEvent class to client.on() registers nothing.
The migration work that isn't code: replace long keyterm lists with a context step that passes what you already know about the encounter, and re-tune downstream extraction logic you built to compensate for your previous model's error patterns. That second one usually takes longer than the API swap.
Drop a clinical recording into the playground with Medical Mode on and read the drug names yourself.
PHI, BAA, and deployment options
AssemblyAI is considered a business associate under HIPAA and offers a standard Business Associate Addendum (BAA) for customers processing PHI. Paid accounts can sign it self-serve from the dashboard; the process is in the BAA FAQ. Deepgram signs BAAs for Enterprise customers.
The parts worth comparing line by line with any vendor:
- Redaction scope. AssemblyAI's PHI redaction covers audio and transcripts. A redacted transcript next to an unredacted recording isn't a redacted encounter.
- Data residency. EU data residency is available at
api.eu.assemblyai.com, at the same price. - Self-hosting. Available when audio can't leave your infrastructure. Confirm Medical Mode availability for your deployment with sales.
- Retention and training. Ask explicitly whether your audio is retained and whether it's used for model training. Get it in writing.
Pricing and how the bill works
Medical Mode is a $0.15/hr add-on on the base model rate: Universal-3.5 Pro is $0.21/hr and Universal-3.6 Pro Realtime is $0.45/hr. Current rates are on the pricing page.
The headline rate isn't the thing to compare. Ask both vendors three questions:
- Is the medical option an add-on on the flagship model, or a different model with a different accuracy profile?
- What does the annual commit look like, and what happens if you undershoot it?
- What's the effective rate at your projected volume, including any minimum?
Where the rest of the field lands
Most evaluations aren't a two-horse race. On the same use-case table, Speechmatics Enhanced Medical records 3.6% MER, AWS Transcribe Medical 8.7% and Google Medical Conversation 24.4%. ElevenLabs Scribe v2 isn't in that table. Microsoft Dragon Copilot and Abridge are products rather than APIs, so if you're building your own scribe they're your competitors, not your vendors.
The best medical speech-to-text roundup goes provider by provider, and the medical transcription API guide covers the selection process more broadly. If you're building an ambient scribe specifically, the speech-to-text API for ambient scribes guide covers far-field and diarization settings.
What changes next
The benchmark gap between the leading medical models will keep narrowing; that's what happens in every accuracy race. The durable advantage is shifting to context, which is a different kind of competition.
Once a model's performance on an encounter depends on what you tell it beforehand, the vendor comparison stops being "whose model is better" and becomes "whose model best uses the data I already have." A 31% reduction in missed terms from a prior-visit note isn't a model improvement; it's an integration improvement. Teams that build patient-context assembly into their transcription layer now will see accuracy improve without changing vendors. Teams that don't will keep re-running bakeoffs to chase a point they could have gotten from their own database.
Get help designing the eval, picking the right entity list and running the comparison properly, including a migration estimate if you decide to switch.
Frequently Asked Questions (FAQs)
Medical Mode vs Nova-3 Medical: which is more accurate?
On the medical benchmark published on AssemblyAI's medical transcription use case page, Universal-3.5 Pro with Medical Mode records a 3.2% Missed Entity Rate and Deepgram Nova-3 Medical records 4.7%. Medical WER on the same table is 5.3% vs 6.1%. Run both on your own encounters before deciding, and score entities rather than overall WER.
How does each handle drug names?
Deepgram routes clinical audio to a separate model, Nova-3 Medical, and takes keyterm lists for specific terms. AssemblyAI's Medical Mode is an LLM post-processing pass that uses the surrounding clinical context to correct misspelled medication names and, to a lesser extent, disease names after transcription. It doesn't measurably change dosages, tests or procedures. In batch tests averaged over 9 sets in 4 languages, Deepgram nova-3-medical missed 26.6% of drug names and AssemblyAI with Medical Mode missed 10.7%. In streaming, the figures were 29.4% and 13.0%, per AssemblyAI's Medical Mode research. Keyterms and free-text context prompting add a second layer at the model input.
Does Deepgram's medical model work in streaming?
Yes. Nova-3 Medical supports pre-recorded and streaming audio. Deepgram's Flux streaming model is built for voice agents and isn't a medical model. On AssemblyAI, Medical Mode uses the same domain: "medical-v1" parameter on pre-recorded audio and on Universal-3.6 Pro Realtime streaming.
Can I keep keyterm lists with Medical Mode?
Yes. Keyterms are a model input and Medical Mode is a post-processing pass, so they work together. On long audio, keep the list to about 200 terms or fewer, because very long lists start adding false insertions. Passing encounter context as free text is often a better use of the same effort.
Does AssemblyAI sign a BAA?
Yes. AssemblyAI is considered a business associate under HIPAA and offers a standard Business Associate Addendum (BAA) for customers processing PHI. Paid accounts can review and sign it self-serve from the dashboard, without a sales call.
What does migrating from Deepgram involve?
The API swap is a config change: a new client, speech_models or speech_model, and domain: "medical-v1" in place of the nova-3-medical model ID. The real work is replacing long keyterm lists with a context step and re-tuning downstream extraction calibrated to your old model's errors. Most teams run both in parallel on live traffic for a couple of weeks before cutting over.