cd /news/ai-products/openrouter-x-assemblyai-universal-3-… · home topics ai-products article
[ARTICLE · art-138588] src=assemblyai.com ↗ pub= topic=ai-products verified=true sentiment=↑ positive

OpenRouter x AssemblyAI: Universal‑3.5 Pro is now available on OpenRouter

AssemblyAI's Universal-3.5 Pro speech-to-text model is now available on OpenRouter as `assemblyai/universal-3-5-pro`, served through AssemblyAI's Sync Speech-to-Text API at AssemblyAI's published rates with no markup added by OpenRouter. The model supports the same 19 languages and is reached via a single POST to OpenRouter's transcription endpoint, with three AssemblyAI options — `prompt` (up to 6,000 characters), `keyterms_prompt`, and `conversation_context` (up to 500 turns or 16,000 characters) — passed in `provider.options.assemblyai`.

read7 min views1 publishedSep 23, 2026
OpenRouter x AssemblyAI: Universal‑3.5 Pro is now available on OpenRouter
Image: source

The speech-to-text call at the front of your pipeline now runs on the OpenRouter key you already have. Same model, same 19 languages, our published rate with no markup.

A voice agent captures a turn the moment endpointing fires, and nothing downstream can start until the words come back. A dictation feature has to put text on screen while the user is still holding the key down. A phone tree captures one utterance and branches the call on what it heard. A push-to-talk message leaves the moment the user lifts their thumb. In all of them, transcription runs first, and every model after it reasons about the words transcription handed over. A wrong word there is a wrong answer three calls later, and the LLM has no way to know it.

The model layer behind that first call has consolidated everywhere except the first call itself. If you build on OpenRouter, you already reach dozens of models through one key, one billing relationship, and one request shape. Speech-to-text sat outside it — a separate vendor, a separate contract, a separate SDK — wrapped around the one component that determines what the rest of your stack sees.

Today, Universal-3.5 Pro is available on OpenRouter, served through our Sync Speech-to-Text API. It is listed on OpenRouter’s transcription endpoint as assemblyai/universal-3-5-pro. Pricing matches our published rates, and OpenRouter passes it through without a markup.

One POST to the endpoint you already call #

One request, one transcript, no job to poll and no WebSocket to hold open:

import base64
import os
import requests

with open("turn.wav", "rb") as f:
    audio = base64.b64encode(f.read()).decode("utf-8")

response = requests.post(
    "https://openrouter.ai/api/v1/audio/transcriptions",
    headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}"},
    json={
        "model": "assemblyai/universal-3-5-pro",
        "input_audio": {"data": audio, "format": "wav"},
    },
    timeout=30,
)
response.raise_for_status()
print(response.json()["text"])

That is the whole integration. If you are already calling OpenRouter for chat completions, the path and the model string are the only things that change. The endpoint also accepts OpenAI-style multipart requests, so an existing OpenAI SDK client pointed at OpenRouter’s base URL works against Universal-3.5 Pro without a code change. Word timestamps come back through OpenRouter’s own response_format and timestamp_granularities fields rather than ours, which keeps the response shape identical to every other transcription model on the endpoint.

Three ways to tell the model what it is about to hear #

Three AssemblyAI options travel in provider.options.assemblyai: prompt, keyterms_prompt, and conversation_context. They are what change what you get back on hard audio, and they are the difference between a transcript that is broadly right and one that gets your customer’s vocabulary right.

prompt describes the situation before the first word arrives. Up to 6,000 characters telling the model what kind of speech is coming: your domain, your product, the shape of the conversation. “Transcribe this medical intake conversation” primes the decoder for clinical vocabulary, which is exactly the ground where a general-purpose model guesses and gets it plausibly wrong. Plausibly wrong is the expensive failure, because it survives every check downstream of it.

conversation_context passes the preceding turns with each request, up to 500 turns or 16,000 characters, and the model transcribes the current clip against that history. When a caller answers “yeah, the second one” or reads back a number the agent just asked for, the model knows what question it is answering. You are only billed for the current turn’s audio, so carrying the full dialogue costs nothing but request size.

keyterms_prompt takes up to 100 terms per request and biases the model toward strings you already know: product names, drug names, the SKUs that belong to this one account. Because they are sent per request, they can be scoped to the individual conversation instead of baked into a global config, which matters when your vocabulary differs per customer.

curl https://openrouter.ai/api/v1/audio/transcriptions \
  -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "assemblyai/universal-3-5-pro",
    "input_audio": { "format": "wav", "data": "<base64 16-bit WAV>" },
    "response_format": "verbose_json",
    "timestamp_granularities": ["word"],
    "provider": {
      "options": {
        "assemblyai": {
          "prompt": "Transcribe this medical intake conversation. The audio is in English.",
          "keyterms_prompt": ["Trazodone", "Tirzepatide", "Semaglutide"],
          "conversation_context": [
            "Thanks for calling. How can I help?",
            "I need to check on a prescription refill.",
            "Sure, which medication?"
          ]
        }
      }
    }
  }'

Built for one turn of speech and an answer right now #

The Sync API is shaped like the request a voice product actually makes: one turn in, one transcript back, in the same response. Called directly, it returns a finished transcript in ~134 ms at p50, against the 5 to 6 seconds of job overhead a submit-and-poll flow adds to audio that takes two seconds to play. A request routed through OpenRouter carries one additional network hop on top of that, so budget accordingly and measure your own p50 from your own region before you wire it into a turn loop.

Clips run from 80 ms to 2 minutes, up to 40 MB, only as WAV. That bound is the design rather than a limit we are working around: audio longer than a turn belongs on the Async API, and a live session that stays open belongs on Realtime. Sync covers the case where the audio is already short and the answer is needed before the user notices waiting.

The accuracy every model after it inherits #

Marketplaces make models look interchangeable, and transcription is the one call in the stack where they are not. Universal-3.5 Pro ranks first for accuracy across independent speech-to-text benchmarks, and it posts a 1.59% normalized word error rate on short-form audio, which is the exact audio profile a turn-shaped request produces.

Proper nouns, alphanumerics, email addresses, and postal addresses are the strings a caller spells out and an agent has to act on, and they are where the gap between transcription models shows up as a failed task rather than a lower score. The model is the same one running behind our Async, Realtime, and Sync surfaces, at the same checkpoint, in all 19 native languages. Routing it through OpenRouter changes the billing relationship and the request shape. It does not change the model.

Key details #

| Model string | assemblyai/universal-3-5-pro | | Endpoint | POST https://openrouter.ai/api/v1/audio/transcriptions | | Request formats | JSON with base64 input_audio, or OpenAI-style multipart | | Auth | Your existing OpenRouter API key | | Clip length | From 80 ms up to 2 minutes of audio per request | | File size | Up to 40 MB | | Input | WAV only, 16 kHz default, auto-resampled | | Languages | 19 native languages, English by default | | AssemblyAI options | prompt, keyterms_prompt, and conversation_context, passed in provider.options.assemblyai | | Timestamps | OpenRouter’s response_format: "verbose_json" with timestamp_granularities: ["word"] | | Latency | ~134 ms p50 measured on our direct Sync endpoint, plus OpenRouter routing | | Price | $0.45/hr of audio, matching our published rates |

Pricing #

$0.45 per hour of audio, billed by duration, matching the Sync API rate on our pricing page. For the first week, pricing will be 50% off of list via OpenRouter.

Get started #

If you have an OpenRouter key, the Python snippet above runs as written. If you want the surfaces OpenRouter doesn’t front, start with a free AssemblyAI account and read the Sync API guide. Both reach the same model.

FAQ #

Is this the same model as AssemblyAI’s own API?

Yes. Same model, same checkpoint, same 19 languages. OpenRouter fronts the Sync API; it does not host a separate copy.

Do I need an AssemblyAI account?

No. Your OpenRouter key is the only credential involved, and billing runs through OpenRouter. You need an AssemblyAI account only for the surfaces OpenRouter doesn’t front.

Does OpenRouter mark up the price?

No. $0.45/hr of audio is our published Sync rate, passed through.

Can I use my existing OpenAI SDK client?

Yes. Point it at OpenRouter’s base URL and set the model string. The endpoint accepts OpenAI-style multipart requests.

How do I get word timestamps?

Set response_format: "verbose_json" and timestamp_granularities: ["word"]. Those are OpenRouter’s fields, not ours.

── more in #ai-products 4 stories · sorted by recency
── more on @assemblyai 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/openrouter-x-assembl…] indexed:0 read:7min 2026-09-23 ·