> ## Documentation Index
> Fetch the complete documentation index at: https://assemblyai.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Transcribe live audio

> Upload audio to the Sync API as it is recorded, so the transcript is ready almost as soon as the speaker stops.

## Overview

[`transcribe()`](/docs/sync-stt/getting-started/transcribe-a-short-audio-file) needs the whole clip before it can send anything. Live upload starts the request immediately and uploads audio as your code produces it, so authorization, the upload, and every speech segment but the last are done by the time the speaker stops. What is left to wait for is the final segment.

The SDKs expose this two ways. Use a **session** when your audio arrives through a callback — a microphone library, a WebRTC track, a telephony media stream:

<Tabs groupId="language">
  <Tab language="python-sdk" title="Python SDK" default>
    ```python theme={null}
    from assemblyai.sync.v1 import SyncTranscriber, SyncTranscriptionConfig

    transcriber = SyncTranscriber()
    config = SyncTranscriptionConfig(sample_rate=16000, channels=1)

    session = transcriber.open_live(config)
    # ... hand each captured chunk to session.write(chunk) ...
    session.close()          # end the audio
    result = session.result()  # wait for the transcript
    print(result.text)
    ```
  </Tab>

  <Tab language="javascript-sdk" title="JavaScript SDK">
    ```javascript theme={null}
    import { AssemblyAI } from "assemblyai";

    const client = new AssemblyAI({ apiKey: process.env.ASSEMBLYAI_API_KEY });

    const session = client.sync.openLive({ sample_rate: 16000, channels: 1 });
    // ... hand each captured chunk to session.write(chunk) ...
    session.close(); // end the audio
    const result = await session.result(); // wait for the transcript
    console.log(result.text);
    ```
  </Tab>
</Tabs>

Or hand it an iterable or stream you already have, and wait for the result:

<Tabs groupId="language">
  <Tab language="python-sdk" title="Python SDK" default>
    ```python theme={null}
    result = transcriber.transcribe_live(chunks, config=config)
    print(result.text)
    ```
  </Tab>

  <Tab language="javascript-sdk" title="JavaScript SDK">
    ```javascript theme={null}
    const result = await client.sync.transcribeLive(chunks, {
      sample_rate: 16000,
      channels: 1,
    });
    console.log(result.text);
    ```
  </Tab>
</Tabs>

Both return the same result as `transcribe()` and post to `POST https://sync.assemblyai.com/v1/transcribe/live`.

<Note>
  **For short audio only**

  Live upload is for short clips: the Sync API caps audio at 120 seconds, and it
  returns one finished transcript when the audio ends. If you need words back
  *while* the speaker is still talking, or you need to capture more than 2 minutes
  of audio, use the
  [Real-time STT API](/docs/streaming/getting-started/transcribe-streaming-audio), which
  opens a WebSocket connection for up to 3 hours.

  It is worth using only when the audio is genuinely still being produced —
  streaming a file that already exists on disk is slower than
  [`transcribe()`](/docs/sync-stt/getting-started/transcribe-a-short-audio-file), which
  sends it in one piece.
</Note>

## Before you begin

To complete this guide, you need:

* **An API key** — copy it from [API Keys](https://www.assemblyai.com/dashboard/home) and set it once:

  ```bash theme={null}
  export ASSEMBLYAI_API_KEY=<your-key>
  ```

* **A live audio source** — a microphone, a recorder process, an in-progress call. This guide uses the microphone.

* **Python 3.8+** for the Python SDK, or **Node.js 18+** for the JavaScript SDK.

* Microphone examples below use [`sounddevice`](https://python-sounddevice.readthedocs.io/) (Python) and [SoX](https://sourceforge.net/projects/sox/) (JavaScript). Any capture library works — all you need is a callback that hands you audio chunks.

## Transcribe from a microphone

A capture library calls you back with a chunk of audio each time one is ready. Open a session, `write()` each chunk from the callback, then `close()` and collect the `result()` when the speaker stops. Raw microphone audio is PCM, which carries no header, so set `sample_rate` and `channels` on the config.

<Tabs groupId="language">
  <Tab language="python-sdk" title="Python SDK" default>
    ```python expandable theme={null}
    import queue
    import threading

    import sounddevice as sd  # pip install sounddevice

    from assemblyai.sync.v1 import SyncTranscriber, SyncTranscriptionConfig

    RATE = 16000
    config = SyncTranscriptionConfig(sample_rate=RATE, channels=1)


    def microphone(stop: threading.Event):
        """Yields PCM chunks from the default microphone until stop is set."""
        chunks: "queue.Queue[bytes]" = queue.Queue()
        with sd.RawInputStream(
            samplerate=RATE,
            channels=1,
            dtype="int16",
            callback=lambda data, *_: chunks.put(bytes(data)),
        ):
            while not stop.is_set():
                try:
                    yield chunks.get(timeout=0.1)
                except queue.Empty:
                    pass


    stop = threading.Event()
    threading.Thread(
        target=lambda: (input("Recording, press Enter to stop... "), stop.set()),
        daemon=True,
    ).start()

    result = SyncTranscriber().transcribe_live(microphone(stop), config=config)
    print(result.text)
    ```
  </Tab>

  <Tab language="javascript-sdk" title="JavaScript SDK">
    ```javascript expandable theme={null}
    import { spawn } from "node:child_process";
    import { AssemblyAI } from "assemblyai";

    const client = new AssemblyAI({ apiKey: process.env.ASSEMBLYAI_API_KEY });
    const RATE = 16000;

    // SoX writes raw 16-bit mono PCM to stdout while it records.
    const recorder = spawn("sox", [
      "--default-device", "--no-show-progress",
      "--rate", String(RATE), "--channels", "1",
      "--encoding", "signed-integer", "--bits", "16",
      "--type", "raw", "-",
    ]);

    const session = client.sync.openLive({ sample_rate: RATE, channels: 1 });

    // write() never blocks, so it is safe to call from the capture callback.
    recorder.stdout.on("data", (chunk) => session.write(chunk));
    recorder.stdout.on("end", () => session.close()); // ends the audio

    console.log("Recording, press Ctrl+C to stop...");
    process.on("SIGINT", () => recorder.kill()); // stop speaking, stop recording

    const result = await session.result(); // closes if needed, then waits
    console.log(result.text);
    ```
  </Tab>
</Tabs>

<Note>
  Leaving the session in the normal way (`close()`, or the recorder ending) uploads
  everything you sent. To throw a recording away without transcribing it — the user
  cancelled, the call dropped — use `session.abort()` instead; `result()` then raises.
</Note>

## Pull from a stream you already have

When your audio already comes as something you can iterate — an async generator, a recorder process's stdout, a `ReadableStream` — hand it straight to `transcribe_live()` (Python) / `transcribeLive()` (JavaScript). It reads the source to the end, then returns the transcript. `open_live()` is this method with a queue in front of it for callback sources.

<Tabs groupId="language">
  <Tab language="python-sdk" title="Python SDK" default>
    ```python theme={null}
    import subprocess

    from assemblyai.sync.v1 import SyncTranscriber, SyncTranscriptionConfig

    config = SyncTranscriptionConfig(sample_rate=16000, channels=1)

    # A recorder process still writing audio to its stdout.
    recorder = subprocess.Popen(
        ["rec", "-q", "-t", "raw", "-r", "16000", "-c", "1",
         "-b", "16", "-e", "signed", "-"],
        stdout=subprocess.PIPE,
    )

    result = SyncTranscriber().transcribe_live(recorder.stdout, config=config)
    print(result.text)
    ```
  </Tab>

  <Tab language="javascript-sdk" title="JavaScript SDK">
    ```javascript theme={null}
    import { spawn } from "node:child_process";
    import { AssemblyAI } from "assemblyai";

    const client = new AssemblyAI({ apiKey: process.env.ASSEMBLYAI_API_KEY });

    // A recorder process still writing audio to its stdout (a Node Readable).
    const recorder = spawn("rec", [
      "-q", "-t", "raw", "-r", "16000", "-c", "1",
      "-b", "16", "-e", "signed", "-",
    ]);

    const result = await client.sync.transcribeLive(recorder.stdout, {
      sample_rate: 16000,
      channels: 1,
    });
    console.log(result.text);
    ```
  </Tab>
</Tabs>

The Python method takes any iterable of `bytes` (synchronous or `async`, file objects included); `AsyncSyncTranscriber.transcribe_live()` is the `asyncio` counterpart. The JavaScript method takes an async iterable, a Node stream, or a web `ReadableStream<Uint8Array>`. Both reject a whole buffer, a `Blob`, or a path by name — audio you already hold belongs in [`transcribe()`](/docs/sync-stt/getting-started/transcribe-a-short-audio-file).

## What to keep in mind

* **Keep producing until you're done.** An upload that goes silent for too long is aborted server-side. Finish by ending the stream (or calling `close()`), not by pausing it.
* **The saving comes from overlap.** All but the last speech segment are transcribed while you record, so the win grows with clip length. Below roughly a minute there is only one segment, so the only saving is the elided upload.
* **Errors can surface mid-upload.** Authorization, rate-limit, and capacity failures can arrive part-way through the upload rather than at the end, as a `SyncTranscriptError`. Call [`warm()`](/docs/sync-stt/connection-pre-warming) before you start recording to open the connection ahead of time.
* **The audio limit is unchanged.** The Sync API still caps audio at 120 seconds. The default request budget is 180 seconds, covering the recording as well as the transcription.

## Next steps

* [Transcribe a short audio file](/docs/sync-stt/getting-started/transcribe-a-short-audio-file) — the buffered path, for audio you already hold whole
* [Connection pre-warming](/docs/sync-stt/connection-pre-warming) — open the connection before recording starts
* [Prompting and keyterms](/docs/sync-stt/prompting-and-keyterms) — improve accuracy with contextual prompts and keyterm biasing
* [Word timestamps](/docs/sync-stt/word-timestamps) — get per-word `start`/`end` timings
* [Error handling](/docs/sync-stt/error-handling) — status codes and retry guidance
* [API reference](/docs/api-reference/sync-api/transcribe-live) — full endpoint documentation

## Need help?

If you get stuck, contact our support team at [support@assemblyai.com](mailto:support@assemblyai.com) or create a [support ticket](https://www.assemblyai.com/contact/support). Include the `session_id` from the response to help us look up your request.
