> This page is for Audio Decisions.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://dev.hume.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://dev.hume.ai/_mcp/server.

# Python quickstart

**Ask questions about a recording from Python.** The script below asks one question of each primitive about the same audio and prints every answer.

<table>
  <tbody>
    <tr>
      <th width="20%">
        Package
      </th>

      <td>
        [`hume-audio-decisions`](https://pypi.org/project/hume-audio-decisions/)

         on PyPI
      </td>
    </tr>

    <tr>
      <th>
        Source
      </th>

      <td>
        [hume-audio-decisions-python-sdk](https://github.com/HumeAI/hume-audio-decisions-python-sdk)

         on GitHub
      </td>
    </tr>

    <tr>
      <th>
        Python
      </th>

      <td>
        3.10 or later
      </td>
    </tr>
  </tbody>
</table>

## Prerequisites

1. Python 3.10 or later.
2. An API key from the [API keys page](https://cloud.hume.ai/settings/organization/api-keys) of the Hume Platform. See [Authentication](/audio-decisions/docs/authentication).
3. A recording of speech 5 seconds to 5 minutes long, in MP3 or WAV.

## Install

Create a project and install the SDK in it. To add the SDK to an existing project, run only the last command.

#### uv

#### uv

```bash
mkdir decisions-quickstart
cd decisions-quickstart
uv init
uv add hume-audio-decisions
```

#### pip

#### pip

```bash
mkdir decisions-quickstart
cd decisions-quickstart
python3 -m venv .venv
source .venv/bin/activate
pip install hume-audio-decisions
```

## Set your API key

The client reads the key from the `HUME_API_KEY` environment variable and sends it in the `X-Hume-Api-Key` header of every request.

#### Shell

```bash
export HUME_API_KEY=your_api_key
```

To pass the key explicitly instead, construct the client with `DecisionsClient(api_key="...")`.

## Ask questions about a recording

#### Create the client

Create `ask_questions.py`. It asks four questions about `speech.wav` in one request and prints each answer.

`DecisionsClient()` reads your API key from `HUME_API_KEY`. The script imports a question class and an answer class for each primitive, and `ApiError` for failed requests.

**`ask_questions.py (lines 1-16)`**

```python ask_questions.py (lines 1-16)
from hume_audio_decisions import (
    Answer_Choice,
    Answer_Noul,
    Answer_Score,
    Answer_Span,
    Criterion,
    DecisionsClient,
    DecisionsRequest,
    Question_Choice,
    Question_Noul,
    Question_Score,
    Question_Span,
)
from hume_audio_decisions.core import ApiError

client = DecisionsClient()
```

#### Write the questions

Each question is keyed by an ID you choose, and its class names its primitive. `noul` asks yes or no, `choice` picks from unordered options, `score` places the audio on ordered levels, and `span` finds when an event occurs. To describe the recording, such as the setting or who is speaking, pass `context` to `DecisionsRequest` alongside `questions`. [Primitives](/audio-decisions/docs/primitives) covers what each question takes.

**`ask_questions.py (lines 18-39)`**

```python ask_questions.py (lines 18-39)
request = DecisionsRequest(
    questions={
        "hesitant": Question_Noul(
            instructions="Does the speaker sound hesitant?",
        ),
        "tone": Question_Choice(
            instructions="Which best describes the speaker's tone?",
            criteria=[
                Criterion(name="warm"),
                Criterion(name="neutral"),
                Criterion(name="curt"),
            ],
        ),
        "naturalness": Question_Score(
            instructions="How natural does the voice sound?",
            criteria=["Not at all", "2", "3", "4", "Extremely"],
        ),
        "vocal_bursts": Question_Span(
            instructions="When does the speaker laugh, sigh or make another vocal burst?",
        ),
    },
)
```

#### Send the recording

`client.decisions.create()` sends the questions and the audio as a `multipart/form-data` request and returns the parsed response. The tuple gives the file part a filename and a content type. To send an MP3 file, pass `audio/mpeg`.

**`ask_questions.py (lines 41-46)`**

```python ask_questions.py (lines 41-46)
try:
    with open("speech.wav", "rb") as audio:
        response = client.decisions.create(
            file=("speech.wav", audio, "audio/wav"),
            request=request,
        )
```

#### Handle errors

A failed request raises an `ApiError`. `status_code` holds the HTTP status, and `body` holds the error body, whose `code` says what went wrong and whose `param` says where in the request it was found. See [Errors](/audio-decisions/docs/errors).

**`ask_questions.py (lines 47-48)`**

```python ask_questions.py (lines 47-48)
except ApiError as error:
    print(f"{error.status_code}: {error.body}")
```

#### Read the answers

The response returns each answer under its question's ID, and the `run_id` of the model run that answered them. A `noul` answer is the probability of yes, `choice` and `score` answers give the probability of each option or level, and a `span` answer lists the time spans where the event may occur.

**`ask_questions.py (lines 50-73)`**

```python ask_questions.py (lines 50-73)
    for question_id, answer in response.answers.items():
        if isinstance(answer, Answer_Noul):
            print(f"{question_id}: yes {answer.noul:.2f}")
        elif isinstance(answer, Answer_Choice):
            print(
                f"{question_id}: {answer.choice}, "
                f"confidence {answer.confidence:.2f}"
            )
        elif isinstance(answer, Answer_Score):
            level = max(
                answer.probabilities,
                key=answer.probabilities.__getitem__,
            )
            print(
                f"{question_id}: {answer.score:.2f}, "
                f"most likely {answer.legend[level]!r}"
            )
        elif isinstance(answer, Answer_Span):
            for span in answer.spans:
                print(
                    f"{question_id}: {span.audio_start_ms} to "
                    f"{span.audio_end_ms} ms, {span.probability:.2f}"
                )
    print(f"run {response.run_id}")
```

#### Complete files

**`ask_questions.py`**

```python title="ask_questions.py"
from hume_audio_decisions import (
    Answer_Choice,
    Answer_Noul,
    Answer_Score,
    Answer_Span,
    Criterion,
    DecisionsClient,
    DecisionsRequest,
    Question_Choice,
    Question_Noul,
    Question_Score,
    Question_Span,
)
from hume_audio_decisions.core import ApiError

client = DecisionsClient()

request = DecisionsRequest(
    questions={
        "hesitant": Question_Noul(
            instructions="Does the speaker sound hesitant?",
        ),
        "tone": Question_Choice(
            instructions="Which best describes the speaker's tone?",
            criteria=[
                Criterion(name="warm"),
                Criterion(name="neutral"),
                Criterion(name="curt"),
            ],
        ),
        "naturalness": Question_Score(
            instructions="How natural does the voice sound?",
            criteria=["Not at all", "2", "3", "4", "Extremely"],
        ),
        "vocal_bursts": Question_Span(
            instructions="When does the speaker laugh, sigh or make another vocal burst?",
        ),
    },
)

try:
    with open("speech.wav", "rb") as audio:
        response = client.decisions.create(
            file=("speech.wav", audio, "audio/wav"),
            request=request,
        )
except ApiError as error:
    print(f"{error.status_code}: {error.body}")
else:
    for question_id, answer in response.answers.items():
        if isinstance(answer, Answer_Noul):
            print(f"{question_id}: yes {answer.noul:.2f}")
        elif isinstance(answer, Answer_Choice):
            print(
                f"{question_id}: {answer.choice}, "
                f"confidence {answer.confidence:.2f}"
            )
        elif isinstance(answer, Answer_Score):
            level = max(
                answer.probabilities,
                key=answer.probabilities.__getitem__,
            )
            print(
                f"{question_id}: {answer.score:.2f}, "
                f"most likely {answer.legend[level]!r}"
            )
        elif isinstance(answer, Answer_Span):
            for span in answer.spans:
                print(
                    f"{question_id}: {span.audio_start_ms} to "
                    f"{span.audio_end_ms} ms, {span.probability:.2f}"
                )
    print(f"run {response.run_id}")
```

## Run

#### uv

#### uv

```bash
uv run ask_questions.py
```

#### pip

#### pip

```bash
python ask_questions.py
```

Each probability estimates the share of human raters who would give that answer, so a `noul` answer near 0.5 means raters would split on the audio, not that the model is unsure. Ask the same questions in the same order when you compare recordings, since an answer can change when other questions are added, removed or reordered.

## Audio helpers

`create` converts a WAV file to 16 kHz mono before sending it, because the model hears audio as one channel at 16 kHz. The converted file is smaller, so a WAV file over the size limit of 25,000,000 bytes can still fit in a request. Pass `downmix_strategy="first_channel"` to keep only the first channel, such as to judge one speaker of a call apart from the other, or `downmix_strategy=None` to send the file as it is. MP3 files are always sent as they are. See [Preparing audio](https://github.com/HumeAI/hume-audio-decisions-python-sdk#preparing-audio) in the SDK README.

A request takes up to 5 minutes of audio. To send a longer recording, `create_chunked` cuts it into chunks of up to `max_chunk_ms` and sends each chunk in its own request.

#### long_recording.py

```python
from hume_audio_decisions import (
    Answer_Span,
    DecisionsClient,
    DecisionsRequest,
    Question_Span,
)

client = DecisionsClient()

with open("long_call.wav", "rb") as audio:
    result = client.decisions.create_chunked(
        file=("long_call.wav", audio, "audio/wav"),
        request=DecisionsRequest(
            questions={
                "vocal_bursts": Question_Span(
                    instructions="When does the speaker laugh, sigh or make another vocal burst?",
                ),
            },
        ),
        max_chunk_ms=300_000,
    )

for chunk in result.chunks:
    if chunk.response is None:
        print(f"{chunk.start_ms} to {chunk.end_ms} ms: {chunk.status}")
        continue
    answer = chunk.response.answers["vocal_bursts"]
    if isinstance(answer, Answer_Span):
        for span in answer.spans:
            start_ms = chunk.start_ms + span.audio_start_ms
            end_ms = chunk.start_ms + span.audio_end_ms
            print(
                f"vocal_bursts: {start_ms} to {end_ms} ms, "
                f"{span.probability:.2f}"
            )
```

The model answers each chunk on its own, and nothing combines the answers across chunks. A span's times count from the start of its chunk, so the script adds the chunk's `start_ms` to place each span in the recording. Most failed requests stop the job, and the chunks after one are not sent; `result.resume_ms` says where to continue. See [Long recordings](https://github.com/HumeAI/hume-audio-decisions-python-sdk#long-recordings) in the SDK README for choosing a chunk length, where the cuts fall and resuming.

## Next steps

#### [Writing questions](/audio-decisions/docs/writing-questions)

Word instructions and criteria so questions get useful answers.

#### [Interpreting answers](/audio-decisions/docs/interpreting-answers)

Read each answer and set thresholds for acting on it.