AssemblyAI Python SDK

repository·master·Indexed 41 days ago

https://github.com/assemblyai/assemblyai-python-sdk

A high-level Python interface for transcribing and understanding audio and speech data at scale. Features include support for long-form audio via Transcriber, low-latency short clips via SyncTranscriber, and live audio via RealTimeTranscriber. Supports speaker labels, language detection, custom spelling, and subtitle exports (SRT/VTT). Version 1.0 introduces breaking changes, including the removal of LeMUR and audio-capture extras.

Tokens
24.8K
Snippets
72
Records
81
Agent score
50%

What's inside AssemblyAI

  1. Choose between Synchronous and Asynchronous transcription

    master

    The SDK provides two execution models for transcription:

    1. Synchronous (Transcriber): Methods like transcribe() block the application flow until the transcription is complete. Use transcribe_async() to return a concurrent.futures.Future object which allows non-blocking status checks.
    2. Asynchronous (AsyncTranscriber): Designed for asyncio applications. It uses coroutines instead of threads. Methods are awaited (e.g., await transcribe()).

    Note: A concurrent.futures.Future returned by transcribe_async is not awaitable. In asyncio environments, always use AsyncTranscriber.

  2. Manage default TranscriptionConfig in Transcriber

    master

    You can define a default TranscriptionConfig to reuse settings across multiple transcription calls. You can set this during Transcriber initialization, update it via the .config property, or override it for a specific method call using the config parameter.

    • Set at initialization: aai.Transcriber(config=config)
    • Update existing transcriber: transcriber.config = new_config
    • Override for one call: transcriber.transcribe(audio, config=override_config)
    config = aai.TranscriptionConfig(punctuate=False, format_text=False)
    
    transcriber = aai.Transcriber(config=config)
    
    # will use the same config for all `.transcribe*(...)` operations
    transcriber.transcribe("https://assembly.ai/wildfires.mp3")
  3. Dual-channel streaming with ChannelStreamer

    master

    Use ChannelStreamer to handle multiple live audio sources (e.g., microphone and system audio) in a single streaming session.

    Key Concepts:

    • Mixing: The SDK mixes multiple PCM channels into a single mono stream over one WebSocket. It keeps channels aligned by consuming the shortest buffer; if a source ends, you must call mixer.close_channel(name) to pad it with silence and prevent stalling.
    • Attribution: ChannelStreamer delivers an enriched DualChannelTurnEvent. This allows you to identify the physical source of words via turn.channel and word.channel.
    • Independence: Channel attribution (word.channel) is independent of speaker diarization (word.speaker).
    • Async Support: AsyncChannelStreamer provides an asyncio-native interface with await mixer.stream(...) and await mixer.close_channel(...).

    Workflow:

    1. Initialize RealTimeTranscriber.
    2. Create a ChannelStreamer instance, declaring named channels and the session sample rate.
    3. Register handlers on the mixer (e.g., mixer.on(RealTimeEvents.Turn, handler)).
    4. Connect the client.
    5. Feed PCM data to each channel using mixer.stream(channel_name, pcm_data).
    6. Call mixer.flush() to push trailing audio.
    from assemblyai.streaming.v3 import (
        ChannelStreamer, RealTimeTranscriber, RealTimeTranscriberOptions,
        RealTimeEvents, RealTimeParameters,
    )
    
    def on_turn(client, event):   # event is a DualChannelTurnEvent
        print(f"[{event.channel}] {event.transcript}")
        for w in event.words:
            print(f"  {w.text!r} -> channel={w.channel} speaker={w.speaker}")
    
    client = RealTimeTranscriber(RealTimeTranscriberOptions(api_key="<YOUR_API_KEY>"))
    
    # Declare the channels and the session sample rate (must be pcm_s16le).
    mixer = ChannelStreamer(client, channels=["mic", "system"], sample_rate=16000)
    # Register handlers on the mixer: Turn handlers receive the enriched event, 
    # other events (Begin/Error/…) are forwarded to the client.
    mixer.on(RealTimeEvents.Turn, on_turn)
    client.connect(RealTimeParameters(
        sample_rate=16000, speech_model="universal-3-5-pro", speaker_labels=True,
    ))
    
    # Feed each source separately — e.g. from two capture callbacks. Send
    # continuous PCM for every channel (silence as zeros), at the same rate.
    mixer.stream("mic", mic_pcm)
    mixer.stream("system", system_pcm)
    
    mixer.flush()                    # push trailing buffered audio
    client.disconnect(terminate=True)
  4. Customize Voice Activity Detection (VAD)

    master

    The SDK uses EnergyVad by default. You can supply a custom VAD (e.g., a DNN VAD like Silero) by subclassing VadDetector (implementing process(frame) -> VadResult and reset()) and passing it via ChannelAttributionOptions.create_vad.

    You can also tune the default EnergyVad using:

    • threshold_ratio: Recommended between 2 and 6.
    • noise_floor_alpha
    • hangover_frames

    Use on_vad=callback to observe raw per-frame activity.

  5. Resolve unknown audio channels

    master

    When a word is labeled as "unknown" (meaning no single channel was clearly dominant), you can use ChannelAttributionOptions.resolve_unknown_channels_method to back-fill the channel identity.

    Available methods:

    • "window" (default): Uses the dominant non-"unknown" channel from among ±resolution_window_words neighbors.
    • "speaker-history": Uses session-wide channel evidence (requires speaker_labels).
    • "none": Leaves the word as "unknown".
  6. Use `DictationTranscriber` for short notes

    master

    aai.DictationTranscriber is designed for short dictations (up to 120s). It transcribes audio as it is spoken and can optionally use an LLM to clean up or reformat the text. It uses a single live-upload connection and returns a DictationResponse.

    DictationResponse fields:

    • text: The raw transcript.
    • llm_response: The rewritten text (if an LLM pass was performed).
    • final_text: The text to show the user (the rewrite if available, otherwise the raw transcript).

    Constraints:

    • Audio must be WAV or raw 16-bit PCM.
    • Maximum 120 seconds per request.
    • URLs are not supported; use aai.Transcriber for URL ingestion.
    import assemblyai as aai
    import sounddevice as sd
    
    aai.settings.api_key = "<YOUR_API_KEY>"
    
    RATE = 16000
    config = aai.DictationConfig(sample_rate=RATE, channels=1)
    
    with aai.DictationTranscriber() as transcriber:
        with transcriber.open_live(config) as session:
            microphone = sd.RawInputStream(
                samplerate=RATE, channels=1, dtype="int16",
                callback=lambda data, *_: session.write(bytes(data)),
            )
            with microphone:
                input("Dictating, press Enter to stop... ")
        print(session.result().final_text)
  7. Choose the appropriate transcriber class

    master

    The SDK provides several transcriber classes depending on your use case (audio length, latency requirements, and features needed). Versioned paths (e.g., assemblyai.prerecorded.v2.Transcriber) are preferred for new code, though top-level shortcuts like aai.Transcriber are available.

    ClassUse Case
    assemblyai.prerecorded.v2.TranscriberLong-form audio, URLs, and audio-intelligence (speaker labels, chapters, sentiment, etc.) via polled job API
    assemblyai.prerecorded.v2.AsyncTranscriberSame as Transcriber, but for asyncio code
    assemblyai.sync.v1.SyncTranscriberShort clips (≤120s, ≤120s, ≤40MB) for lowest latency. Returns transcript in one request. Supports files or live audio via open_live()/transcribe_live()
    assemblyai.sync.v1.AsyncSyncTranscriberSame as SyncTranscriber, but for asyncio code
    assemblyai.dictation.v1.DictationTranscriberDictation: short spoken notes returned as one transcript with optional LLM rewrite. Supports callbacks or iterators/files/bytes
    assemblyai.dictation.v1.AsyncDictationTranscriberSame as DictationTranscriber, but for asyncio code
    assemblyai.streaming.v3.RealTimeTranscriberLive audio (microphone, telephony, voice agents) via websocket
    assemblyai.streaming.v3.AsyncRealTimeTranscriberSame as RealTimeTranscriber, but for asyncio code
  8. Set a deadline for transcription polling with `poll_timeout=`

    master

    The transcribe method polls until a transcript reaches a terminal status. To prevent a stuck job from hanging your request handler, use the poll_timeout= parameter to set a deadline (in seconds).

    If a timeout occurs, a TranscriptError is raised. Note that the job continues processing on the server; you can resume it later using Transcript.get_by_id(transcript_id).

    from assemblyai import TranscriptError
    from assemblyai.prerecorded.v2 import Transcriber
    
    transcriber = Transcriber(api_key="YOUR_API_KEY")
    
    try:
        transcript = transcriber.transcribe(
            "https://assembly.ai/wildfires.mp3",
            poll_timeout=300,
        )
    except TranscriptError as error:
        # The message carries the transcript id; the job keeps processing server-side.
        print(f"still running: {error}")
  9. Install the AssemblyAI Python SDK

    master

    Install or upgrade the AssemblyAI Python SDK using pip:

    pip install -U assemblyai

    Note for users migrating from 0.x: There are breaking changes in version 1.0. Specifically, LeMUR and audio-capture extras have been removed. Please refer to the MIGRATION.md file for recommended patterns.

  10. Warm sync connections to reduce latency

    master

    Because SyncTranscriber performs a single request, the DNS, TCP, and TLS handshakes occur on the critical path. To minimize latency, call .warm() as soon as you know audio is being recorded. You can also set aai.settings.keepalive_expiry to ensure the connection remains open.

    import assemblyai as aai
    from assemblyai.sync.v1 import SyncTranscriber
    
    aai.settings.keepalive_expiry = 120  # seconds; matches the 120s sync audio cap
    
    transcriber = SyncTranscriber(api_key="YOUR_API_KEY")
    transcriber.warm()  # while the clip is still being recorded
    # ...later: transcriber.transcribe("./call.wav")
  11. Import from versioned submodules

    master

    In 1.0, it is preferred to import transcribers from their specific versioned subpackages to explicitly pin the product and API version.

    Mapping:

    • assemblyai.prerecorded.v2: Provides Transcriber, AsyncTranscriber, Transcript, AsyncTranscript, TranscriptGroup, and TranscriptionConfig.
    • assemblyai.sync.v1: Provides SyncTranscriber, AsyncSyncTranscriber, SyncTranscriptionConfig, SyncTranscriptError, and friends.
    • assemblyai.streaming.v3: Provides RealTime* classes and event types.

    Note: Cross-cutting names like TranscriptStatus, TranscriptError, Settings, Client, AsyncClient, and the global settings should still be imported from the top-level assemblyai package.

    from assemblyai.prerecorded.v2 import Transcriber
    from assemblyai.sync.v1 import SyncTranscriber
    
    transcriber = Transcriber(api_key="YOUR_API_KEY")
    sync_transcriber = SyncTranscriber(api_key="YOUR_API_KEY")
  12. Pass `api_key=` directly to clients

    master

    Instead of relying on global process-wide state via aai.settings.api_key, you should pass the api_key= argument directly when instantiating transcribers or streaming clients.

    If both a client= and an api_key= are provided, the api_key= takes precedence. The transcriber will derive its own client from a copy of the provided client's settings with the key replaced, leaving the original client untouched.

    from assemblyai import Client, Settings
    from assemblyai.prerecorded.v2 import Transcriber
    
    transcriber = Transcriber(api_key="YOUR_API_KEY")
    
    shared = Client(settings=Settings(api_key="TEAM_KEY", http_timeout=60.0))
    reuses_shared = Transcriber(client=shared)                   # `shared` verbatim
    per_tenant = Transcriber(client=shared, api_key="TENANT_KEY")
    # `per_tenant` keeps http_timeout=60.0; `shared` still carries TEAM_KEY.