Skip to content

AssemblyAI Hub API reference (v3.4.0) ​

All paths and bodies match the official AssemblyAI API (https://www.assemblyai.com/docs). This file lists only what the Hub adds or changes.

Hosts ​

Hub pathOfficial service
/v2/*api.assemblyai.com (pre-recorded)
/v3/token, /v3/wsstreaming.assemblyai.com (real-time v3)
/llm-gateway/v1/*llm-gateway.assemblyai.com
/sync/v1/*sync.assemblyai.com
/dictation/v1/*dictation.assemblyai.com
/hub/*, /healthzHub only

EU accounts automatically use the EU hosts.

Auth ​

Any of: Authorization: hub-..., Authorization: Bearer hub-..., ?token=hub-... (WebSocket). Optional X-Hub-Region: us|eu restricts the request to accounts in that region.

What the Hub rewrites ​

  • Authorization → the chosen upstream account's key.
  • webhook_url and its auth header: the Hub receives AssemblyAI's callback, then POSTs {"transcript_id", "status"} to your webhook_url with your header (retries 5x on 5xx / network errors). Responses show your webhook settings, not the Hub's.
  • Legacy speech_model (singular) → speech_models; retired universal-3-pro → universal-3-5-pro.

Everything else passes through, so new AssemblyAI parameters work without a Hub upgrade.

Pre-recorded ​

CallNotes
POST /v2/uploadRaw bytes. The Hub remembers which account holds the file and submits transcripts that use it on the same account. Official limit ~2.2 GB.
POST /v2/transcriptJSON body. Response adds nothing; webhook_url shows your own.
GET /v2/transcript/{id}Routed to the owning account. Adds webhook_status_code (last status from forwarding to your webhook).
DELETE /v2/transcript/{id}Routed to the owning account.
GET /v2/transcriptFrom the Hub's own records across all accounts. Params: limit (max 200), status, before_id, after_id. Same shape as official.
GET /v2/transcript/{id}/{sentences,paragraphs,srt,vtt,word-search,redacted-audio}Routed to the owning account; query params pass through.
Other /v2/*Forwarded to any account.

Ids not created through this Hub return 404.

Models (as of 2026-10) ​

  • universal-3-5-pro: best accuracy, 18 languages incl. Chinese; supports prompt, temperature, format_text: false.
  • universal-2: 99 languages.
  • Retired / rejected: universal-3-pro, slam-1, singular speech_model, summarization, auto_chapters, summary_model, summary_type.
  • Some features (e.g. iab_categories, auto_highlights) are English-centric and error on unsupported languages.

Real-time v3 ​

Direct:

GET /v3/token?expires_in_seconds=60&max_session_duration_seconds=3600
→ {"token": "...", "expires_in_seconds": 60, "websocket_url": "wss://streaming.assemblyai.com/v3/ws"}

Then connect to websocket_url?token=...&sample_rate=16000[&speech_model=...]. expires_in_seconds 1–600, max_session_duration_seconds 60–10800. Audio does not pass through the Hub.

Relay: wss://<hub>/v3/ws?sample_rate=16000&format_turns=true&token=hub-...

  • Query params (except token) and all frames pass through both ways: binary PCM audio, UpdateConfiguration, ForceEndpoint, Terminate; events Begin, Turn, Termination.
  • If the client drops without Terminate, the Hub sends it upstream.
  • Account chosen once per session; no mid-session failover.
  • Close codes: 4001 invalid key, 4003 no upstream account, others are AssemblyAI's.

Minimal relay client (Python, pip install websockets):

python
import asyncio, json, websockets

async def main(url, pcm_path):
    async with websockets.connect(url) as ws:
        async def send():
            with open(pcm_path, "rb") as f:
                while chunk := f.read(3200):          # 100 ms of 16 kHz 16-bit mono
                    await ws.send(chunk); await asyncio.sleep(0.1)
            await ws.send(json.dumps({"type": "Terminate"}))
        task = asyncio.create_task(send())
        async for msg in ws:
            e = json.loads(msg)
            if e.get("type") == "Turn" and e.get("end_of_turn"):
                print(e["transcript"])
        await task

asyncio.run(main("wss://hub.example.com/v3/ws?sample_rate=16000&format_turns=true&token=hub-...", "audio.raw"))

Convert audio to that format: ffmpeg -i in.mp3 -ac 1 -ar 16000 -f s16le audio.raw.

LLM gateway and speech understanding ​

  • POST /llm-gateway/v1/chat/completions: OpenAI-compatible. GET /llm-gateway/v1/models for models. Requires a paid AssemblyAI account.
  • POST /llm-gateway/v1/understanding with {"transcript_id": ..., "speech_understanding": {"request": {...}}}: routed to the account that owns the transcript. Request types include translation (target_languages, formal) and speaker_identification (speaker_type: name or role).

Sync and Dictation ​

POST /sync/v1/transcribe (≤20 min / 256 MB), POST /sync/v1/transcribe/live (≤120 s WAV/PCM), GET /sync/v1/warm, POST /dictation/v1/transcribe, POST /dictation/v1/transcribe/live. Multipart or binary bodies pass through; any account, with failover.

Hub endpoints ​

  • GET /healthz → {"ok": true, "version": "3.4.0"} (no auth)
  • GET /hub/usage → {"key": name, "usage": [{"kind", "events", "seconds", "tokens"}]}; kinds: transcript, stream, token, llm, sync, dictation, api
  • GET /hub/docs, GET /hub/openapi.json when ALLOW_OPEN_DOCS=true

Errors and failover ​

StatusBodyCause
401{"detail": {"error": "Missing API key"}} / "Invalid API key"Hub key
400{"detail": {"error": "Body must be JSON"}}POST /v2/transcript
404{"detail": {"error": "Transcript not found"}}Id unknown to the Hub
503{"error": "hub: No upstream account available"}No usable account
503{"error": "hub: All upstream accounts failed"}Network errors on every tried account
otherAssemblyAI's bodyUpstream error, passed through

Failover: 429 → account cools down COOLDOWN_SECONDS; 401/402/403 → account marked error; 5xx and network errors → retried. Up to MAX_RETRY_ACCOUNTS accounts per request. Requests bound to an existing transcript or upload never switch accounts.

文档版本 v3.4.0 · 与项目版本同步