> ## 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 a short audio file

> Learn how to transcribe an audio file synchronously with the Sync API.

## Overview

Send an audio file in a single call, get a transcript back in milliseconds. No polling, no session management. The SDKs wrap the whole round trip in one method:

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

    from assemblyai.sync.v1 import SyncTranscriber

    transcriber = SyncTranscriber(api_key=os.environ["ASSEMBLYAI_API_KEY"])
    result = transcriber.transcribe("./sample.wav")
    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 result = await client.sync.transcribe("./sample.wav");
    console.log(result.text);
    ```
  </Tab>
</Tabs>

The Sync API uses our flagship Universal-3.5 Pro model and accepts audio from 80 milliseconds up to 120 seconds. It transcribes with state-of-the-art accuracy in [19 languages](/docs/sync-stt/language-selection#supported-languages).

Not using an SDK? The same flow works over plain HTTP — see [Using the HTTP API directly](#using-the-http-api-directly).

<Note>
  **When to use Sync STT**

  Sync STT is ideal for pre-recorded audio clips under 2 minutes where you need
  an immediate response without polling — for example, voice message transcription,
  short call recordings, or voice agent pipelines that handle turn detection
  externally and submit completed utterances for transcription. For audio longer
  than 120 seconds, use [Pre-recorded STT](/docs/pre-recorded-audio/getting-started/transcribe-an-audio-file).
  For live microphone audio, use [Real-time STT](/docs/streaming/getting-started/transcribe-streaming-audio).
</Note>

## Before you begin

To complete this guide, you need:

* **An API key** — browse to [API Keys](https://www.assemblyai.com/dashboard/home) in your dashboard and copy your key. Every example below reads it from an environment variable, so set it once:

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

* **An audio file** in WAV format (or raw PCM S16LE), between 80 milliseconds and 120 seconds long.

* **Python 3.8+** for the Python SDK, or **Node.js 18+** for the JavaScript SDK. The HTTP examples at the bottom of this page need Python 3.8+, Node.js 18+, or cURL.

## Transcribe your first file

### Step 1: Install the SDK

<Tabs groupId="language">
  <Tab language="python-sdk" title="Python SDK" default>
    ```bash theme={null}
    pip install -U assemblyai
    ```
  </Tab>

  <Tab language="javascript-sdk" title="JavaScript SDK">
    ```bash theme={null}
    npm install assemblyai
    ```
  </Tab>
</Tabs>

### Step 2: Run your first transcription

Save this as `transcribe.py` (Python) or `transcribe.mjs` (JavaScript), next to a `sample.wav` file:

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

    from assemblyai.sync.v1 import SyncTranscriber

    transcriber = SyncTranscriber(api_key=os.environ["ASSEMBLYAI_API_KEY"])
    result = transcriber.transcribe("./sample.wav")
    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 result = await client.sync.transcribe("./sample.wav");
    console.log(result.text);
    ```
  </Tab>
</Tabs>

Then run it — `python transcribe.py` or `node transcribe.mjs`. You'll see the transcript printed:

```text theme={null}
Hi, I'm calling about my Best Buy order...
```

`transcribe()` accepts a local file path or raw audio bytes — an open binary file object in Python, or a `Uint8Array`, `Blob`, or readable stream in JavaScript — but not a URL. The Sync API has no URL ingestion; for remote files, download them first or use [Pre-recorded STT](/docs/pre-recorded-audio/getting-started/transcribe-an-audio-file).

## Customize your request

The call above works with no extra configuration. Pass a config to change how the audio is transcribed — in Python a `SyncTranscriptionConfig`, either as the transcriber's default (`SyncTranscriber(config=...)`) or per call (`transcribe(data, config=...)`); in JavaScript, a plain object as the second argument to `transcribe()`.

### Select a model

`model` selects the sync speech model and defaults to `universal-3-5-pro`. Set it explicitly to pin the model:

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

    config = SyncTranscriptionConfig(model="universal-3-5-pro")
    result = transcriber.transcribe("./sample.wav", config=config)
    ```
  </Tab>

  <Tab language="javascript-sdk" title="JavaScript SDK">
    ```javascript theme={null}
    const result = await client.sync.transcribe("./sample.wav", {
      model: "universal-3-5-pro",
    });
    ```
  </Tab>
</Tabs>

### Choose a language

Pass [`language_codes`](/docs/sync-stt/language-selection) as a list of ISO 639-1 codes — one code for monolingual audio, several for multilingual audio:

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

    config = SyncTranscriptionConfig(language_codes=["es"])
    result = transcriber.transcribe("./sample.wav", config=config)
    ```
  </Tab>

  <Tab language="javascript-sdk" title="JavaScript SDK">
    ```javascript theme={null}
    const result = await client.sync.transcribe("./sample.wav", {
      language_codes: ["es"],
    });
    ```
  </Tab>
</Tabs>

### Get word timestamps

Per-word timings are opt-in. Set [`timestamps=True`](/docs/sync-stt/word-timestamps) to compute `start`/`end` for every word, at a small latency cost. Without it, words carry `text` and `confidence` only:

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

    config = SyncTranscriptionConfig(timestamps=True)
    result = transcriber.transcribe("./sample.wav", config=config)

    for word in result.words:
        print(word.text, word.start, word.end)  # milliseconds
    ```
  </Tab>

  <Tab language="javascript-sdk" title="JavaScript SDK">
    ```javascript theme={null}
    const result = await client.sync.transcribe("./sample.wav", {
      timestamps: true,
    });

    for (const word of result.words) {
      console.log(word.text, word.start, word.end); // milliseconds
    }
    ```
  </Tab>
</Tabs>

### Transcribe raw PCM audio

WAV files carry their sample rate and channel count in the file header. Raw PCM (S16LE little-endian) doesn't, so set `sample_rate` and `channels` on the config — both are required, and setting either one routes the audio as raw PCM:

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

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

    with open("./sample.pcm", "rb") as f:
        result = transcriber.transcribe(f.read(), config=config)

    print(result.text)
    ```
  </Tab>

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

    const audio = readFileSync("./sample.pcm");

    const result = await client.sync.transcribe(audio, {
      sample_rate: 16000,
      channels: 1,
    });

    console.log(result.text);
    ```
  </Tab>
</Tabs>

## Complete example

Here's the complete, runnable script — the call above plus options and error handling:

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

    from assemblyai.sync.v1 import (
        SyncTranscriber,
        SyncTranscriptError,
        SyncTranscriptionConfig,
    )

    config = SyncTranscriptionConfig(
        model="universal-3-5-pro",
        language_codes=["en"],
        timestamps=True,
    )

    with SyncTranscriber(api_key=os.environ["ASSEMBLYAI_API_KEY"]) as transcriber:
        try:
            result = transcriber.transcribe("./sample.wav", config=config)
        except SyncTranscriptError as error:
            # error_code is machine-readable (bad_audio, audio_too_large,
            # capacity_exceeded, inference_timeout, ...); retry_after is set on
            # 429/503 responses. See /sync-stt/error-handling
            raise RuntimeError(
                f"Transcription failed ({error.status_code}/{error.error_code}): {error}"
            ) from error

        # Record session_id for every request, not just errors — it's the first
        # thing support@assemblyai.com asks for.
        print(f"session_id: {result.session_id}")
        print(f"\nFull Transcript:\n\n{result.text}")

        for word in result.words:
            print(f"{word.text} ({word.confidence:.2f}) {word.start}-{word.end}ms")
    ```
  </Tab>

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

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

    try {
      const result = await client.sync.transcribe("./sample.wav", {
        model: "universal-3-5-pro",
        language_codes: ["en"],
        timestamps: true,
      });

      // Record session_id for every request, not just errors — it's the first
      // thing support@assemblyai.com asks for.
      console.log(`session_id: ${result.session_id}`);
      console.log(`\nFull Transcript:\n\n${result.text}`);

      for (const word of result.words) {
        console.log(
          `${word.text} (${word.confidence.toFixed(2)}) ${word.start}-${word.end}ms`
        );
      }
    } catch (error) {
      if (error instanceof SyncTranscriptError) {
        // errorCode is machine-readable (bad_audio, audio_too_large,
        // capacity_exceeded, inference_timeout, ...); retryAfter is set on
        // 429/503 responses. See /sync-stt/error-handling
        throw new Error(
          `Transcription failed (${error.status}/${error.errorCode}): ${error.message}`
        );
      }
      throw error;
    }
    ```
  </Tab>
</Tabs>

<Note>
  Calling the Sync API repeatedly? `warm()` opens the connection ahead of time
  so your next `transcribe()` doesn't pay the DNS, TCP, and TLS handshake on the
  critical path. See [Connection pre-warming](/docs/sync-stt/connection-pre-warming).
</Note>

## What you get back

`transcribe()` returns a `SyncTranscriptResponse` with the transcript and word-level details:

| Attribute                  | Description                                                                                                                                                                                 |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `result.text`              | Full transcript of the audio.                                                                                                                                                               |
| `result.words`             | Per-word `text` and `confidence`, plus `start`/`end` timestamps (in milliseconds) when the request sets [`timestamps=True`](/docs/sync-stt/word-timestamps). Otherwise `start`/`end` are `None`. |
| `result.confidence`        | Overall transcript confidence score (0–1).                                                                                                                                                  |
| `result.audio_duration_ms` | Duration of the submitted audio in milliseconds.                                                                                                                                            |
| `result.session_id`        | Server-generated UUID. Include this when contacting support.                                                                                                                                |
| `result.request_time_ms`   | End-to-end server-side processing time for the request, in milliseconds.                                                                                                                    |

Failed requests raise `SyncTranscriptError`, which carries the HTTP status, a machine-readable error code (for example `bad_audio`, `audio_too_large`, `capacity_exceeded`, `inference_timeout`), and a retry-after value in seconds for `429`/`503` responses — named `status_code`/`error_code`/`retry_after` in the Python SDK and `status`/`errorCode`/`retryAfter` in the JavaScript SDK. See [Error handling](/docs/sync-stt/error-handling) for status codes and retry guidance.

## Using the HTTP API directly

If you prefer to call the Sync API over HTTP without an SDK, post the audio to `POST https://sync.assemblyai.com/transcribe` and read the transcript out of the JSON response.

The Sync API accepts `multipart/form-data` with an `audio` part. For WAV files, set the part's Content-Type to `audio/wav`. Authenticate with your key in the `Authorization` header (no `Bearer` prefix), and send the `X-AAI-Model: universal-3-5-pro` header — it is required on every request.

<Tabs groupId="language">
  <Tab language="curl" title="cURL" default>
    Replace `<YOUR_API_KEY>` in the request header:

    ```bash theme={null}
    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'
    ```

    The response JSON is printed to stdout. Pipe to `jq` to extract fields:

    ```bash theme={null}
    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' \
      | jq '.text'
    ```
  </Tab>

  <Tab language="python" title="Python">
    Install the `requests` library if you haven't already:

    ```bash theme={null}
    pip install requests
    ```

    Send the audio file and parse the response:

    ```python expandable theme={null}
    import requests

    with open("sample.wav", "rb") as f:
        audio = f.read()

    response = requests.post(
        "https://sync.assemblyai.com/transcribe",
        headers={
            "Authorization": "<YOUR_API_KEY>",
            "X-AAI-Model": "universal-3-5-pro",
        },
        files={
            "audio": ("sample.wav", audio, "audio/wav"),
        },
        timeout=60,
    )
    response.raise_for_status()
    result = response.json()
    print(result["text"])
    print(result["session_id"])
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript expandable theme={null}
    import { readFileSync } from "fs";

    const audio = readFileSync("sample.wav");
    const form = new FormData();
    form.append("audio", new Blob([audio], { type: "audio/wav" }), "sample.wav");

    const response = await fetch("https://sync.assemblyai.com/transcribe", {
      method: "POST",
      headers: {
        Authorization: "<YOUR_API_KEY>",
        "X-AAI-Model": "universal-3-5-pro",
      },
      body: form,
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.message || error.detail);
    }

    const result = await response.json();
    console.log(result.text);
    console.log(result.session_id);
    ```
  </Tab>
</Tabs>

### Response shape

A successful request returns a JSON object with the transcript and word-level details:

```json theme={null}
{
  "text": "Hi, I'm calling about my Best Buy order...",
  "words": [
    { "text": "Hi",  "confidence": 0.91 },
    { "text": "I'm", "confidence": 0.88 }
  ],
  "confidence": 0.87,
  "audio_duration_ms": 101567,
  "session_id": "eb92c4ff-4bbb-429f-9b99-7279d7fe738f",
  "request_time_ms": 243.7
}
```

| Field               | Description                                                                                                                                   |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `text`              | Full transcript of the audio.                                                                                                                 |
| `words`             | Per-word `confidence`, plus `start`/`end` timestamps (in milliseconds) when the request enables [word timestamps](/docs/sync-stt/word-timestamps). |
| `confidence`        | Overall transcript confidence score (0–1).                                                                                                    |
| `audio_duration_ms` | Duration of the submitted audio in milliseconds.                                                                                              |
| `session_id`        | Server-generated UUID. Include this when contacting support.                                                                                  |
| `request_time_ms`   | End-to-end server-side processing time for the request, in milliseconds.                                                                      |

### Sending raw PCM audio

When your audio is raw PCM (S16LE little-endian), set the `audio` part Content-Type to `audio/pcm` and include `sample_rate` and `channels` in the `config` part:

<Tabs groupId="language">
  <Tab language="curl" title="cURL" default>
    ```bash theme={null}
    curl -X POST https://sync.assemblyai.com/transcribe \
      -H 'Authorization: <YOUR_API_KEY>' \
      -H 'X-AAI-Model: universal-3-5-pro' \
      -F 'audio=@sample.pcm;type=audio/pcm' \
      -F 'config={"sample_rate":16000,"channels":1};type=application/json'
    ```
  </Tab>

  <Tab language="python" title="Python">
    ```python theme={null}
    import json
    import requests

    with open("sample.pcm", "rb") as f:
        audio = f.read()

    response = requests.post(
        "https://sync.assemblyai.com/transcribe",
        headers={
            "Authorization": "<YOUR_API_KEY>",
            "X-AAI-Model": "universal-3-5-pro",
        },
        files={
            "audio": ("sample.pcm", audio, "audio/pcm"),
            "config": (
                None,
                json.dumps({"sample_rate": 16000, "channels": 1}),
                "application/json",
            ),
        },
        timeout=60,
    )
    response.raise_for_status()
    print(response.json()["text"])
    ```
  </Tab>

  <Tab language="javascript" title="JavaScript">
    ```javascript theme={null}
    import { readFileSync } from "fs";

    const audio = readFileSync("sample.pcm");
    const form = new FormData();
    form.append("audio", new Blob([audio], { type: "audio/pcm" }), "sample.pcm");
    form.append(
      "config",
      new Blob(
        [JSON.stringify({ sample_rate: 16000, channels: 1 })],
        { type: "application/json" }
      )
    );

    const response = await fetch("https://sync.assemblyai.com/transcribe", {
      method: "POST",
      headers: {
        Authorization: "<YOUR_API_KEY>",
        "X-AAI-Model": "universal-3-5-pro",
      },
      body: form,
    });

    const result = await response.json();
    console.log(result.text);
    ```
  </Tab>
</Tabs>

## Next steps

* [Prompting and keyterms](/docs/sync-stt/prompting-and-keyterms) — improve accuracy with contextual prompts and keyterm biasing
* [Conversation context](/docs/sync-stt/conversation-context) — pass prior dialogue for multi-turn continuity
* [Language selection](/docs/sync-stt/language-selection) — transcribe in any of the 19 supported languages
* [Word timestamps](/docs/sync-stt/word-timestamps) — get per-word `start`/`end` timings
* [Audio requirements](/docs/sync-stt/audio-requirements) — duration, size, and format constraints
* [Connection pre-warming](/docs/sync-stt/connection-pre-warming) — cut the handshake off the critical path
* [Error handling](/docs/sync-stt/error-handling) — status codes and retry guidance
* [Cloud endpoints & data residency](/docs/sync-stt/endpoints-and-data-zones) — use the EU endpoint for data residency
* [API reference](/docs/api-reference/sync-api/transcribe) — 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.
