Appearance
For clean Markdown of any page, append .md to the page URL. For a complete documentation index, see For full documentation content, see For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at
Universal-3 Pro (Async)
For the complete documentation index, see llms.txt
Model ID: universal-3-proDescription: Our most powerful Voice AI model, designed to capture the "hard stuff" that traditional ASR models struggle with. Delivers state-of-the-art accuracy for entities, rare words, and domain-specific terminology out of the box, with code switching and optional prompting for more control. Also our fastest model, so you get the best accuracy without sacrificing speed. Supported languages for pre-recorded: English (en), Spanish (es), French (fr), German (de), Italian (it), Portuguese (pt). Supported regions: US and EU.
cURL quickstart:
bash
curl \
--header "Authorization: YOUR_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"audio_url": "",
"speech_models": ["universal-3-pro"]
}'For broadest language coverage, use "speech_models": ["universal-3-pro", "universal-2"] to fall back to Universal-2 for unsupported languages.
Universal-3 Pro is our most powerful Voice AI model, designed to capture the "hard stuff" that traditional ASR models struggle with. It delivers state-of-the-art accuracy for entities, rare words, and domain-specific terminology out of the box, with code switching and optional prompting for more control. It's also our fastest model, so you get the best accuracy without sacrificing speed.
Quickstart
Get started with Universal-3 Pro using the code below. This example transcribes a pre-recorded audio file and prints the transcript text to your terminal.
Install the required library
bash
pip install requestsCreate a new file main.py and paste the code below. Replace `` with your API key.
Run with python main.py.
python
import requests
import time
base_url = ""
headers = {"authorization": "<YOUR_API_KEY>"}
data = {
"audio_url": "",
"language_detection": True,
"speech_models": ["universal-3-pro", "universal-2"]
}
response = requests.post(base_url + "/v2/transcript", headers=headers, json=data)
if response.status_code != 200:
print(f"Error: {response.status_code}, Response: {response.text}")
response.raise_for_status()
transcript_response = response.json()
transcript_id = transcript_response["id"]
polling_endpoint = f"{base_url}/v2/transcript/{transcript_id}"
while True:
transcript = requests.get(polling_endpoint, headers=headers).json()
if transcript["status"] == "completed":
print(transcript["text"])
break
elif transcript["status"] == "error":
raise RuntimeError(f"Transcription failed: {transcript['error']}")
else:
time.sleep(3)Install the required library
bash
pip install "assemblyai>=1.0.0"Create a new file main.py and paste the code below. Replace `` with your API key.
Run with python main.py.
python
import assemblyai as aai
aai.settings.api_key = "<YOUR_API_KEY>"
audio_file = ""
config = aai.TranscriptionConfig(
speech_models=["universal-3-pro", "universal-2"],
language_detection=True,
)
transcript = aai.Transcriber().transcribe(audio_file, config)
print(transcript.text)Create a new file index.mjs and paste the code below. Replace `` with your API key.
Run with node index.mjs.
javascript
const baseUrl = "";
const headers = {
authorization: "<YOUR_API_KEY>",
};
const data = {
audio_url: "",
language_detection: true,
speech_models: ["universal-3-pro", "universal-2"],
};
const url = `${baseUrl}/v2/transcript`;
let res = await fetch(url, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error(`Error: ${res.status}`);
const response = await res.json();
const transcriptId = response.id;
const pollingEndpoint = `${baseUrl}/v2/transcript/${transcriptId}`;
while (true) {
res = await fetch(pollingEndpoint, { headers });
if (!res.ok) throw new Error(`Error: ${res.status}`);
const transcriptionResult = await res.json();
if (transcriptionResult.status === "completed") {
console.log(transcriptionResult.text);
break;
} else if (transcriptionResult.status === "error") {
throw new Error(`Transcription failed: ${transcriptionResult.error}`);
} else {
await new Promise((resolve) => setTimeout(resolve, 3000));
}
}Install the required library
bash
npm install assemblyaiCreate a new file index.mjs and paste the code below. Replace `` with your API key.
Run with node index.mjs.
javascript
import { AssemblyAI } from "assemblyai";
const client = new AssemblyAI({
apiKey: "<YOUR_API_KEY>",
});
const audioFile = "";
const params = {
audio: audioFile,
speech_models: ["universal-3-pro", "universal-2"],
language_detection: true,
};
const run = async () => {
const transcript = await client.transcripts.transcribe(params);
console.log(transcript.text);
};
run();Universal-3 Pro supports English, Spanish, Portuguese, French, German, and Italian. To access all 99 languages, use "speech_models": ["universal-3-pro", "universal-2"] as shown in the code example. Read more here.
Key capabilities
The model out of the box outperforms all ASR models on the market on accuracy, especially as it pertains to entities and rare words. With prompting, you can get an entirely customized transcription output that rivals near-human-level transcription.
- Keyterm Prompting: Improve recognition of domain-specific terminology, rare words, and proper nouns
- Prompting: Guide transcription style, formatting, and output characteristics
| What prompts can do | Description |
|---|---|
| Verbatim transcription and disfluencies | Include um, uh, false starts, repetitions, stutters |
| Output style and formatting | Control punctuation, capitalization, number formatting |
| Context aware clues | Help with jargon, names, and domain expectations |
| Entity accuracy and spelling | Improve accuracy for proper nouns, brands, technical terms |
| Native code switching | Handle multilingual audio in same transcript |
| Regional dialect recognition | Accurately transcribe regional dialects like Quebecois French, Brazilian Portuguese, Spanglish, and more. See supported dialects |
| Numbers and measurements | Control how numbers, percentages, and measurements are formatted |
To fine-tune to your use case, see the Prompting section. Not sure where to start? Use one of the recommended prompts and tweak from there.
Start with no prompt
We strongly recommend testing with no prompt first. When you omit the prompt parameter, Universal-3 Pro automatically applies a built-in default prompt that is already optimized for accuracy across a wide range of audio types — including verbatim transcription, multilingual code-switching, and challenging audio conditions. For most use cases, the default prompt delivers excellent results out of the box.
If you're going to build a prompt, start with one of the recommended prompts and then tweak it for your use case. You should not start from scratch with your prompt — use a recommended prompt and then build off of it. Please read the Prompting Guide (Async) if you'd like to build your prompt yourself.
Remember, prompts are primarily instructional, so adding a large amount of context may not make a significant impact on accuracy and could reduce instruction-following coherence. Feel free to layer in additional instructions that you see in the Prompting Guide (Async).
Benchmarking
Across the industry, we're seeing that as models improve, they sometimes capture words or phrases that human transcribers originally missed. In WER evaluations, this shows up as insertions, even when the model is technically correct. We've also seen substitutions impact scores in cases where formatting differs (e.g., "alright" vs. "all right"), despite no meaningful accuracy difference.
To help address this, we're actively developing documentation, blog content, and benchmarking tools focused on best practices for evaluating modern speech-to-text systems. We'll continue sharing these resources as they're released.
This is increasingly becoming an industry-wide benchmarking challenge as models begin to match, or exceed, human transcription quality in certain scenarios.
For more details on evaluating transcription accuracy, including tips on using semantic WER and handling substitution artifacts, see Evaluating your prompts in the Prompting Guide.
Keyterms prompting
Keyterms prompting allows you to provide up to 1,000 words or phrases (maximum 6 words per phrase) using the keyterms_prompt parameter to improve transcription accuracy for those terms and related variations or contextually similar phrases.
If you already know the specific names, brands, drug names, acronyms, or jargon that will appear in your audio, reach for keyterms_prompt — it is optimized for term boosting and produces more reliable results than describing the same terms in a free-form prompt. Use the prompt parameter when you want to control transcription style or behavior (disfluencies, formatting, code switching). The two parameters cannot be used in the same request.
Here is an example showing how you can use keyterms prompting to improve transcription accuracy for a name with distinctive spelling and formatting.
Without keyterms prompting:
txt
Hi, this is Kelly Byrne DonahueWith keyterms prompting:
txt
Hi, this is Kelly Byrne-Donoghuepython
import requests
import time
base_url = ""
headers = {"authorization": "<YOUR_API_KEY>"}
data = {
"audio_url": "",
"language_detection": True,
"speech_models": ["universal-3-pro", "universal-2"],
"keyterms_prompt": ["Kelly Byrne-Donoghue"]
}
response = requests.post(base_url + "/v2/transcript", headers=headers, json=data)
if response.status_code != 200:
print(f"Error: {response.status_code}, Response: {response.text}")
response.raise_for_status()
transcript_response = response.json()
transcript_id = transcript_response["id"]
polling_endpoint = f"{base_url}/v2/transcript/{transcript_id}"
while True:
transcript = requests.get(polling_endpoint, headers=headers).json()
if transcript["status"] == "completed":
print(transcript["text"])
break
elif transcript["status"] == "error":
raise RuntimeError(f"Transcription failed: {transcript['error']}")
else:
time.sleep(3)python
import assemblyai as aai
aai.settings.api_key = "<YOUR_API_KEY>"
audio_file = ""
config = aai.TranscriptionConfig(
speech_models=["universal-3-pro", "universal-2"],
language_detection=True,
keyterms_prompt=["Kelly Byrne-Donoghue"],
)
transcript = aai.Transcriber().transcribe(audio_file, config)
print(transcript.text)javascript
const baseUrl = "";
const headers = {
authorization: "<YOUR_API_KEY>",
};
const data = {
audio_url: "",
language_detection: true,
speech_models: ["universal-3-pro", "universal-2"],
keyterms_prompt: ["Kelly Byrne-Donoghue"],
};
const url = `${baseUrl}/v2/transcript`;
let res = await fetch(url, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error(`Error: ${res.status}`);
const response = await res.json();
const transcriptId = response.id;
const pollingEndpoint = `${baseUrl}/v2/transcript/${transcriptId}`;
while (true) {
res = await fetch(pollingEndpoint, { headers });
if (!res.ok) throw new Error(`Error: ${res.status}`);
const transcriptionResult = await res.json();
if (transcriptionResult.status === "completed") {
console.log(transcriptionResult.text);
break;
} else if (transcriptionResult.status === "error") {
throw new Error(`Transcription failed: ${transcriptionResult.error}`);
} else {
await new Promise((resolve) => setTimeout(resolve, 3000));
}
}javascript
import { AssemblyAI } from "assemblyai";
const client = new AssemblyAI({
apiKey: "<YOUR_API_KEY>",
});
const audioFile = "";
const params = {
audio: audioFile,
speech_models: ["universal-3-pro", "universal-2"],
language_detection: true,
keyterms_prompt: ["Kelly Byrne-Donoghue"],
};
const run = async () => {
const transcript = await client.transcripts.transcribe(params);
console.log(transcript.text);
};
run();Remove audio tags
Universal-3 Pro generates rich transcripts that can include inline annotations such as audio event markers (e.g., [laughter], [music]) and speaker cues. If your workflow requires clean, undecorated text, set remove_audio_tags to "all" to strip all inline annotations from the transcript output.
This is especially useful for:
- Pipelines that parse transcript text downstream (NLP, search indexing, LLM input)
- Display contexts where annotations would confuse end users
- Any workflow that expects plain text output
python
import requests
import time
base_url = ""
headers = {"authorization": "<YOUR_API_KEY>"}
data = {
"audio_url": "",
"language_detection": True,
"speech_models": ["universal-3-pro", "universal-2"],
"remove_audio_tags": "all"
}
response = requests.post(base_url + "/v2/transcript", headers=headers, json=data)
if response.status_code != 200:
print(f"Error: {response.status_code}, Response: {response.text}")
response.raise_for_status()
transcript_response = response.json()
transcript_id = transcript_response["id"]
polling_endpoint = f"{base_url}/v2/transcript/{transcript_id}"
while True:
transcript = requests.get(polling_endpoint, headers=headers).json()
if transcript["status"] == "completed":
print(transcript["text"])
break
elif transcript["status"] == "error":
raise RuntimeError(f"Transcription failed: {transcript['error']}")
else:
time.sleep(3)python
import assemblyai as aai
aai.settings.api_key = "<YOUR_API_KEY>"
audio_file = ""
config = aai.TranscriptionConfig(
speech_models=["universal-3-pro", "universal-2"],
language_detection=True,
remove_audio_tags="all",
)
transcript = aai.Transcriber().transcribe(audio_file, config)
print(transcript.text)javascript
const baseUrl = "";
const headers = {
authorization: "<YOUR_API_KEY>",
};
const data = {
audio_url: "",
language_detection: true,
speech_models: ["universal-3-pro", "universal-2"],
remove_audio_tags: "all",
};
const url = `${baseUrl}/v2/transcript`;
let res = await fetch(url, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error(`Error: ${res.status}`);
const response = await res.json();
const transcriptId = response.id;
const pollingEndpoint = `${baseUrl}/v2/transcript/${transcriptId}`;
while (true) {
res = await fetch(pollingEndpoint, { headers });
if (!res.ok) throw new Error(`Error: ${res.status}`);
const transcriptionResult = await res.json();
if (transcriptionResult.status === "completed") {
console.log(transcriptionResult.text);
break;
} else if (transcriptionResult.status === "error") {
throw new Error(`Transcription failed: ${transcriptionResult.error}`);
} else {
await new Promise((resolve) => setTimeout(resolve, 3000));
}
}javascript
import { AssemblyAI } from "assemblyai";
const client = new AssemblyAI({
apiKey: "<YOUR_API_KEY>",
});
const audioFile = "";
const params = {
audio: audioFile,
speech_models: ["universal-3-pro", "universal-2"],
language_detection: true,
remove_audio_tags: "all",
};
const run = async () => {
const transcript = await client.transcripts.transcribe(params);
console.log(transcript.text);
};
run();This parameter is only supported for Universal-3 Pro.
Prompting
For a comprehensive guide on crafting effective prompts, including best practices, prompt capabilities, and example prompts, see the Prompting guide.
Universal-3 Pro delivers great accuracy out of the box. To fine-tune transcription results to your use case, provide a prompt with up to 1,500 words of context in plain language. This helps the model consistently recognize domain-specific terminology, apply your preferred formatting conventions, handle code switching between languages, and better interpret ambiguous speech.
Use prompt to control transcription style or behavior (disfluencies, formatting, code switching). If you already know the specific names, brands, drug names, acronyms, or jargon that will appear in your audio, use keyterms_prompt instead — it is optimized for term boosting and produces more reliable results than describing the same terms in plain language. The two parameters cannot be used in the same request.
The following is our recommended prompt for verbatim multi-lingual transcription:
python
import requests
import time
base_url = ""
headers = {"authorization": "<YOUR_API_KEY>"}
data = {
"audio_url": "",
"language_detection": True,
"speech_models": ["universal-3-pro", "universal-2"],
"prompt": "Required: Preserve the original language(s) and script as spoken, "
"including code-switching and mixed-language phrases.\n\n"
"Mandatory: Preserve linguistic speech patterns including disfluencies, "
"filler words, hesitations, repetitions, stutters, false starts, "
"and colloquialisms in the spoken language.\n\n"
"Always: Transcribe speech with your best guess based on context in "
"all possible scenarios where speech is present in the audio."
}
response = requests.post(base_url + "/v2/transcript", headers=headers, json=data)
if response.status_code != 200:
print(f"Error: {response.status_code}, Response: {response.text}")
response.raise_for_status()
transcript_response = response.json()
transcript_id = transcript_response["id"]
polling_endpoint = f"{base_url}/v2/transcript/{transcript_id}"
while True:
transcript = requests.get(polling_endpoint, headers=headers).json()
if transcript["status"] == "completed":
print(transcript["text"])
break
elif transcript["status"] == "error":
raise RuntimeError(f"Transcription failed: {transcript['error']}")
else:
time.sleep(3)python
import assemblyai as aai
aai.settings.api_key = "<YOUR_API_KEY>"
audio_file = ""
config = aai.TranscriptionConfig(
speech_models=["universal-3-pro", "universal-2"],
language_detection=True,
prompt="Required: Preserve the original language(s) and script as spoken, "
"including code-switching and mixed-language phrases.\n\n"
"Mandatory: Preserve linguistic speech patterns including disfluencies, "
"filler words, hesitations, repetitions, stutters, false starts, "
"and colloquialisms in the spoken language.\n\n"
"Always: Transcribe speech with your best guess based on context in "
"all possible scenarios where speech is present in the audio.",
)
transcript = aai.Transcriber().transcribe(audio_file, config)
print(transcript.text)javascript
const baseUrl = "";
const headers = {
authorization: "<YOUR_API_KEY>",
};
const data = {
audio_url: "",
language_detection: true,
speech_models: ["universal-3-pro", "universal-2"],
prompt:
"Required: Preserve the original language(s) and script as spoken, " +
"including code-switching and mixed-language phrases.\n\n" +
"Mandatory: Preserve linguistic speech patterns including disfluencies, " +
"filler words, hesitations, repetitions, stutters, false starts, " +
"and colloquialisms in the spoken language.\n\n" +
"Always: Transcribe speech with your best guess based on context in " +
"all possible scenarios where speech is present in the audio.",
};
const url = `${baseUrl}/v2/transcript`;
let res = await fetch(url, {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error(`Error: ${res.status}`);
const response = await res.json();
const transcriptId = response.id;
const pollingEndpoint = `${baseUrl}/v2/transcript/${transcriptId}`;
while (true) {
res = await fetch(pollingEndpoint, { headers });
if (!res.ok) throw new Error(`Error: ${res.status}`);
const transcriptionResult = await res.json();
if (transcriptionResult.status === "completed") {
console.log(transcriptionResult.text);
break;
} else if (transcriptionResult.status === "error") {
throw new Error(`Transcription failed: ${transcriptionResult.error}`);
} else {
await new Promise((resolve) => setTimeout(resolve, 3000));
}
}javascript
import { AssemblyAI } from "assemblyai";
const client = new AssemblyAI({
apiKey: "<YOUR_API_KEY>",
});
const audioFile = "";
const params = {
audio: audioFile,
speech_models: ["universal-3-pro", "universal-2"],
language_detection: true,
prompt:
"Required: Preserve the original language(s) and script as spoken, " +
"including code-switching and mixed-language phrases.\n\n" +
"Mandatory: Preserve linguistic speech patterns including disfluencies, " +
"filler words, hesitations, repetitions, stutters, false starts, " +
"and colloquialisms in the spoken language.\n\n" +
"Always: Transcribe speech with your best guess based on context in " +
"all possible scenarios where speech is present in the audio.",
};
const run = async () => {
const transcript = await client.transcripts.transcribe(params);
console.log(transcript.text);
};
run();Default prompt
When no prompt is provided, Universal-3 Pro automatically applies the following default prompt:
txt
Transcribe with context and proper nouns preserved, where speech is
present in the audio. Each language as spoken. English as English.
Non-native speakers.You can override the default prompt by providing your own prompt value. See the Prompting guide for detailed examples covering verbatim transcription, output formatting, entity accuracy, code switching, and more.
The previous built-in system prompt was:
text
Always: Transcribe code-switching speech with your best guess based on
context in all possible scenarios where speech is present in the audio.
Languages: English, Spanish, German, French, Portuguese, Italian.
Language codes: en, es, de, fr, pt, it.The previous built-in system prompt was:
text
Always: Transcribe speech with your best guess based on context in all
possible scenarios where speech is present in the audio.The previous built-in system prompt was:
text
Required: Preserve the original language(s) and script as spoken,
including code-switching and mixed-language phrases.
Mandatory: Preserve linguistic speech patterns including disfluencies,
filler words, hesitations, repetitions, stutters, false starts, and
colloquialisms in the spoken language.
Always: Transcribe speech with your best guess based on context in all
possible scenarios where speech is present in the audio.The previous built-in system prompt was:
text
Transcribe this audioBest practices for prompt engineering
See the Prompting guide for recommended prompts, capability "knobs" you can turn, and guidance on evaluating prompts against your own audio.
Support for 99 languages
With the speech_models parameter, you can list multiple speech models in priority order, allowing our system to automatically route your audio based on language support.
Model routing behavior: The system attempts to use the models in priority order falling back to the next model when needed. For example, with ["universal-3-pro", "universal-2"], the system will try to use universal-3-pro for languages it supports (English, Spanish, Portuguese, French, German, and Italian), and automatically fall back to Universal-2 for all other languages. This ensures you get the best performing transcription where available while maintaining the widest language coverage.