assemblyai logo

assemblyai

assemblyai

assemblyai/assemblyai-skill190installs9stars

SKILL.md

Full skill instructions

AssemblyAI Speech-to-Text and Voice AI

AssemblyAI provides speech-to-text APIs, audio intelligence models, and an LLM Gateway for applying language models to transcripts. This skill corrects common mistakes that training data gets wrong — deprecated APIs, discontinued SDKs, and non-obvious auth patterns.

Authentication

All endpoints use the same header:

Authorization: YOUR_API_KEY

NOT Authorization: Bearer ... — just the raw API key, no Bearer prefix. This is the #1 mistake.

Base URLs

ServiceUSEU
REST API (async)https://api.assemblyai.comhttps://api.eu.assemblyai.com
Sync STT API (≤120s)https://sync.assemblyai.com (global default, routes to nearest; https://sync.us.assemblyai.com for US residency)https://sync.eu.assemblyai.com
LLM Gatewayhttps://llm-gateway.assemblyai.com/v1https://llm-gateway.eu.assemblyai.com/v1
Streaming v3wss://streaming.assemblyai.com/v3/wswss://streaming.eu.assemblyai.com/v3/ws
Streaming v2 (legacy)wss://api.assemblyai.com/v2/realtime/ws
Voice Agent APIwss://agents.assemblyai.com/v1/wswss://agents.eu.assemblyai.com/v1/ws

Streaming EU region: As of March 2026, the EU region moved from AWS eu-west-1 (Ireland) to AWS eu-north-1 (Stockholm). The customer-facing endpoint host (streaming.eu.assemblyai.com) is unchanged.

SDKs

LanguagePackageStatus
Pythonpip install assemblyaiActive
JavaScript/TypeScriptnpm i assemblyaiActive
Rubyassemblyai gemActive
Javaassemblyai-java-sdkDiscontinued April 2025
Goassemblyai-go-sdkDiscontinued April 2025
C# .NETAssemblyAI NuGetDiscontinued April 2025

Only Python, JS/TS, and Ruby SDKs are maintained. For Java, Go, or C#, use the REST API directly.

Speech-to-Text Models

Pre-Recorded

ModelLanguagesBest For
Universal-3.5 Pro18 (auto-falls back to Universal-2 for the other 99)Latest flagship: best accuracy, native code-switching across its 18 languages, contextual prompt, keyterms up to 1,000 words
Universal-3 Pro6 (en, es, de, fr, pt, it)Highest accuracy, promptable transcription, keyterms up to 1,000 words
Universal-299Broadest language coverage, keyterms up to 200 words

Use speech_models as a priority list with fallback: ["universal-3-pro", "universal-2"] (the default when omitted).

Universal-3.5 Pro is also available for pre-recorded/async transcription, not just streaming. Opt in with speech_models: ["universal-3-5-pro"] on POST /v2/transcript. It supports contextual prompt (a plain-language description of the audio — domain/scenario/full detail) and keyterms_prompt (up to 1,000 terms), does native code-switching, and auto-falls back to Universal-2 for languages outside its 18. Note: the formal OpenAPI speech_models enum still lists only universal-3-pro/universal-2, but the async API accepts universal-3-5-pro.

Streaming

ModelLanguagesBest For
universal-3-5-pro18Recommended default for new realtime/streaming code — next-gen flagship: more languages, improved prompting + conversational context
u3-rt-pro6Universal-3 Pro Streaming — punctuation-based turn detection, promptable, mode accuracy/latency tradeoff
universal-streaming-english1 (English)Voice agents, ~300ms latency
universal-streaming-multilingual6Per-utterance language detection
whisper-rt99+Legacy — removed from the public model picker (June 2026) and the streaming spec enums, but still functional via speech_model: whisper-rt for broadest streaming language coverage, auto-detect only

For realtime/streaming STT, use speech_model: "universal-3-5-pro" by default. The raw API parameter is optional and defaults to universal-3-5-pro; set it explicitly when pinning behavior or using an SDK that requires the field. The mode connection param (universal-3-5-pro / u3-rt-pro) trades off accuracy vs latency: min_latency, balanced (default), max_accuracy.

Medical Mode (Add-On)

domain: "medical-v1" enables Medical Mode — an add-on that improves accuracy for medical terminology (medications, procedures, conditions, dosages). Works with both pre-recorded and streaming models.

  • Pre-recorded: Universal-3 Pro (domain: "medical-v1" in request body), Universal-2
  • Streaming: universal-3-5-pro, u3-rt-pro, universal-streaming-english, universal-streaming-multilingual
  • Supported languages: English, Spanish, German, French (4 languages only)
  • Billed as a separate add-on. If used with an unsupported language, the API ignores domain and returns a warning — transcript still completes and you are NOT charged for Medical Mode.

Prompting (Universal-3.5 Pro)

prompt and keyterms_prompt are complementary — use either, or both together. Neither changes the output format, and both work the same way for streaming and async (POST /v2/transcript). Transcription behavior (verbatim, punctuation, formatting) is built in and managed by AssemblyAI.

  • prompt (string, ~1500 chars max): a plain-language description of the audio — its domain, scenario, or full details. It carries context, not instructions — formatting/behavioral commands (punctuation rules, "transcribe verbatim", "don't…") are not supported and are ignored. The model stays grounded in the audio: irrelevant or only-partially-applicable context won't make it insert words that weren't spoken, so you can safely send the same description on every session/segment.
  • keyterms_prompt (string[]): an explicit list of names/brands/domain terms to boost. Streaming: up to 100 terms, ≤50 chars each. Async: up to 1,000 terms.

Contextual prompt — three levels of specificity (use the least specific that covers your case):

LevelLengthContainsExample
Domain2–5 wordsThe field onlyMedical consultation call.
Scenario5–15 wordsWhat the call is aboutCardiology consultation about chest pain symptoms.
Detailed20–50 wordsNames, products, identifiersCardiology consultation between Dr. Smith and a patient about recurring chest pain, ECG results, and hypertension medication.

Best practices:

  • Start with no prompt and no keyterms_prompt — the model is optimized out of the box. Add context only for domain vocabulary it gets wrong, starting at the broadest level.
  • Write plain, complete sentences that describe the recording; keep it to one short block, not a keyword list (that's what keyterms_prompt is for).
  • Keyterms: use exact spelling/capitalization; avoid common words (over-inclusion causes overcorrection/hallucination).
  • Specify language via language_code (preferred) or by naming it in the prompt (e.g. "Spanish customer support call…").
  • Streaming: update both mid-session via UpdateConfiguration; a new keyterms array replaces the prior set, [] clears it.

Sync STT API (short-form audio, ≤120s)

A separate synchronous endpoint for short clips — send audio in one HTTP request, get the transcript back in the response. No polling, no transcript ID, no upload step. Ideal for voice-message transcription, short call recordings, or voice-agent pipelines that do their own turn detection and submit completed utterances.

  • Endpoint: POST https://sync.assemblyai.com/transcribe (global default — routes to nearest region; use sync.us.assemblyai.com / sync.eu.assemblyai.com for data residency)
  • Required header: X-AAI-Model — the current quickstart uses universal-3-5-pro; u3-sync-pro (Universal-3 Pro) is also accepted (the value in the formal spec enum)
  • Auth: Authorization: YOUR_API_KEY (Bearer prefix optional here, unlike the async REST API; or pass ?token=YOUR_API_KEY)
  • Body: multipart/form-data with an audio part (Content-Type: audio/wav or audio/pcm) and an optional config JSON part
  • config fields: prompt (≤4096 chars), word_boost (string[], ≤2048 chars total — this is the documented keyterms param for Sync, not keyterms_prompt), conversation_context (string or string[] — prior conversation turns oldest-first, for continuity across a multi-turn conversation; oldest dropped when over the context budget), language_code (ISO 639-1 string or list, default en — steers the default prompt toward the named language(s); ignored when a custom prompt is set), and for audio/pcm also sample_rate + channels (required for raw PCM; WAV reads them from its header)
  • Audio limits: 80ms–120s, ≤40MB, 16-bit only, mono/stereo (stereo down-mixed), sample rates 8000/16000/22050/24000/32000/44100/48000 Hz
  • Response: { text, words[{text, start, end, confidence}], confidence, audio_duration_ms, session_id } — word timestamps use start/end (integer milliseconds), same field names as the async API; only the clip-level audio_duration_ms carries the _ms suffix
  • 30s per-request deadline (504 inference_timeout). For audio >120s use the async REST API; for live mic audio use Streaming.
curl -X POST https://sync.assemblyai.com/transcribe \
  -H 'Authorization: YOUR_API_KEY' \
  -H 'X-AAI-Model: universal-3-5-pro' \
  -F 'audio=@sample.wav;type=audio/wav'

LeMUR is Deprecated

LeMUR is deprecated (sunset March 31, 2026 — already sunset). Use the LLM Gateway instead. The LLM Gateway is an OpenAI-compatible API. Key difference: you pass transcript text directly in messages (no transcript_ids). Transcribe first, then include transcript.text in your prompt.

See references/llm-gateway.md for models, tool calling, structured outputs, and examples.

Key Gotchas

GotchaDetails
prompt + keyterms_promptComplementary for Universal-3.5 Pro — use either or both together. prompt is a contextual description of the audio; keyterms_prompt is an explicit term list. Neither changes output formatting
summarization / auto_chaptersDeprecated. Use LLM Gateway instead (transcribe → send text to LLM)
PII redaction scopeOnly redacts words in text — other feature outputs (entities, summaries) may still expose sensitive data
Upload key scopingFiles uploaded with one API key project cannot be transcribed with a different project's key
Structured outputsSupported by OpenAI, Gemini, Claude 4.5+, Qwen, and Kimi — Claude 3.x does NOT support json_schema structured outputs
U3 Pro-family turn detectionuniversal-3-5-pro and u3-rt-pro use punctuation (. ? !), NOT confidence thresholds — end_of_turn_confidence_threshold has no effect
prompt is context, not instructionsUniversal-3.5 Pro's prompt describes the audio (domain/scenario/details). Formatting or behavioral commands (punctuation rules, "transcribe verbatim", negative directives like "don't…") are not supported and are ignored — transcription behavior is managed internally
PII audio redaction methodoverride_audio_redaction_method: "silence" replaces PII with silence instead of default beep
Language detectionRequires minimum 15 seconds of spoken audio for reliable results
LLM Gateway EU regionOnly Anthropic Claude and Google Gemini models available — OpenAI models are NOT supported in EU
DisfluenciesEnable with disfluencies: true to keep "um"/"uh" in the transcript
Medical Mode unsupported languageAPI silently skips Medical Mode and does not charge for it — check for warning in response
Voice Agent API URLThe Voice Agent endpoint is wss://agents.assemblyai.com/v1/ws — NOT /v1/voice (renamed April 2026), /v1/realtime (older), or speech-to-speech.us.assemblyai.com (very old)
Voice Agent tool.call fieldThe argument dict is named arguments, not args (renamed April 2026)
Voice Agent stored agents (agent_id)The first session.update either binds a reusable stored agent via {"agent_id":"<id>"} (the only field in session) OR sends inline config (system_prompt/greeting/tools/input/output) — the two modes are mutually exclusive; sending both raises a validation error. Create stored agents with POST https://agents.assemblyai.com/v1/agents
Voice Agent turn detection fieldsUse min_silence (default 1000ms) and max_silence (default 3000ms) under session.input.turn_detectionmin_turn_silence/max_turn_silence are the streaming/LiveKit/Pipecat field names, not Voice Agent API. Both must be in [50, 10000] ms with min_silence < max_silence. Setting either explicitly disables adaptive endpointing for the rest of the session
Voice Agent immutable fieldsAfter session.ready, immutable: greeting, output.voice, output.format — changing them returns immutable_field. Mutable: system_prompt, input.turn_detection, input.keyterms (up to 100 strings), output.volume (0–100), tools, input.format
Voice Agent greetingThe greeting is sent straight to the TTS engine — it is NOT passed through the LLM. Whatever string you set is exactly what the user hears, word for word. Don't write meta-greetings like "Greet the user warmly" — TTS will literally speak that
Voice Agent hold-mode transcriptsWhile an execution_mode: "hold" tool is in flight, transcript.user.delta / transcript.user are NOT emitted in real time — they flush when the hold ends (on tool.result or reply.create)
Voice Agent audio pacingDon't stream audio faster than realtime — excess frames are dropped server-side
Voice Agent session teardown billingJust closing the WebSocket holds the session for 30s (resumable via session.resume) and that grace window is billable. Send session.end ({"type":"session.end"}) when the call is over to close immediately and stop billing — the server replies with a final session.ended (carrying session_duration_seconds, audio_duration_seconds) before closing the socket
Streaming format_turns digit renderingformat_turns=true enables punctuation, casing, and inverse text normalization (dates, times, phone numbers) — it does NOT control digit rendering. Numerals like "22" are a model behavior, and lexical number output ("twenty-two") is not supported in streaming
Streaming EU regionMoved from Ireland (eu-west-1) to Stockholm (eu-north-1) in March 2026. Endpoint host (streaming.eu.assemblyai.com) is unchanged
LLM Gateway tool_calls locationtool_calls lives at choices[i].message.tool_calls (under message), NOT at choices[i].tool_calls (under choice). content is null when only tool_calls are present
LLM Gateway finish_reason is provider-nativeDon't branch tool-calling loops on finish_reason == "tool_calls" — the Gateway passes the provider's value through, so Claude returns tool_use/end_turn (OpenAI returns tool_calls/stop). Detect a tool call by the presence of message.tool_calls, not by finish_reason
Transcript metadata.warningsThe Transcript response now includes an optional metadata object. When present, metadata.warnings is an array of {message} objects describing issues processed during transcription (e.g. Medical Mode skipped due to unsupported language). metadata is omitted entirely when there is nothing to report
U3 Pro-family streaming context carryoverOn by default — the model carries prior finalized turns forward as context (per-session, ~3 entries, ~1500 chars). Pass your agent's spoken reply via agent_context (connection-time query param to seed an opening greeting, or mid-stream via UpdateConfiguration) so the model knows the question the user is answering. Use with universal-3-5-pro or u3-rt-pro
Streaming diarization revised labelsWith speaker_labels enabled, a single SpeakerRevision message is emitted right before Termination (after you send Terminate), containing a revisions array of only the turns whose speaker labels changed (matched by turn_order). Text and word timestamps never change — only speaker assignments. Adds ~400ms latency at session close. Use it for the final, highest-quality attribution
LLM Gateway model_region: "global"Optional request field (only accepted value "global") routes to the provider's global, non-region endpoints for lower cost. Live for Anthropic Claude now; Google Gemini 3 series coming soon. Omit for default in-region processing. Effective July 1, 2026, in-region LLM Gateway requests cost 10% more (provider pass-through, no AssemblyAI upcharge) — global routing keeps current pricing

Common Mistakes

MistakeCorrection
Authorization: Bearer KEYAuthorization: KEY (no Bearer prefix) — BUT the Voice Agent API (agents.assemblyai.com) uses Authorization: Bearer KEY
Using LeMUR APIDeprecated. Use LLM Gateway instead
Using summarization or auto_chaptersDeprecated. Use LLM Gateway instead (transcribe then summarize via LLM)
LeMUR transcript_ids with LLM GatewayPass transcript text in messages, not IDs
anthropic/claude-... model IDsNo provider prefix: claude-sonnet-4-5-20250929 not anthropic/claude-sonnet-4-5-20250929
claude-opus-4-20250514 / claude-sonnet-4-20250514 on LLM GatewayRemoved June 2026. Use Claude Opus 4.5/4.6/4.7 or Claude Sonnet 4.5/4.6
Uploading to /v2/upload with -d/--data or a JSON bodyUse --data-binary @file (raw bytes). -d/JSON returns a valid upload_url but transcription later fails with Transcoding failed. File type application/json
Using Java/Go/C# SDKsDiscontinued. Use Python, JS/TS, Ruby, or raw API
word_boost on the async REST APIUse keyterms_prompt instead. Exception: the Sync STT API does use word_boost (in its config part) — that's its documented keyterms param
Hardcoding v2 streaming URLv3 (/v3/ws) is current; v2 still works but is legacy
Assuming streaming defaults to u3-rt-proStreaming speech_model defaults to universal-3-5-pro at the raw API layer. Set a different model only when you intentionally need legacy behavior, cost tradeoffs, or broader language coverage
Python SDK rejects universal-3-5-proUpgrade to assemblyai>=0.64.21 for Streaming v3 SDK support. Older SDKs such as 0.64.4 validate speech_model against an enum that omits universal-3-5-pro
aai.SpeechModel.universal_3_pro in Python SDKUse raw strings: "universal-3-pro", "universal-2" — these enum aliases don't exist in the SDK
S2S session.update without "session" keyMust wrap config: {"type":"session.update","session":{...}}
S2S tool schema using {"function":{...}} nestingS2S tools are flat: {"type":"function","name":"...","description":"...","parameters":{...}}
Voice Agent S2S URLCorrect URL: wss://agents.assemblyai.com/v1/ws — not /v1/voice (renamed April 2026), /v1/realtime (older), or speech-to-speech.us.assemblyai.com (very old)
Voice Agent tool.call args fieldRenamed to argumentsevent["arguments"] is the parameter dict
Medical Mode domain: "medical"Correct value is domain: "medical-v1"
LLM Gateway tool result role: "function_call_output"Correct role is "tool" — use {"role": "tool", "tool_call_id": "...", "content": "..."}
LLM Gateway response choices[i].tool_callsTool calls live under message: choices[i].message.tool_calls, not at the choice level
Sending tool.result immediately on tool.callWait until reply.done is the latest event received — sending earlier (mid transition phrase) or later (after a new turn started) breaks turn-taking
Speech Understanding without the request wrapperFeatures nest under speech_understanding.request.<feature>speech_understanding.translation (no .request) is invalid. Results come back under speech_understanding.response.<feature>
Custom Formatting params as booleansdate/phone_number/email are format-pattern strings (e.g. "mm/dd/yyyy"), not true/false. Only format_utterances is a boolean

Reference Files

Read the relevant reference file based on what the user needs:

FileWhen to read
references/python-sdk.mdPython SDK patterns and examples
references/js-sdk.mdJavaScript/TypeScript SDK patterns
references/streaming.mdReal-time/streaming STT, v3 protocol, temp tokens, error codes
references/voice-agents.mdVoice agent integrations: LiveKit, Pipecat, turn detection, latency optimization
references/llm-gateway.mdApplying LLMs to transcripts, tool calling, available models
references/speech-understanding.mdTranslation, speaker identification, custom formatting
references/audio-intelligence.mdPII redaction, diarization, summarization, sentiment, chapters
references/api-reference.mdFull parameter list, export endpoints, webhooks, upload, PII policies, Sync STT API, Voice Agents REST API (stored agents)

API Spec Source of Truth

https://github.com/AssemblyAI/assemblyai-api-spec