> This page is for Voice APIs.

> 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.

# EVI Python Quickstart

In this guide, you’ll learn how to use Hume’s Python SDK to integrate with EVI, for applications like command-line or desktop applications.

> **Warning**
>
> **Use the Python SDK for client-side applications** like **CLIs** and **desktop apps** that run directly on the user's machine.
>
> **For hosted environments** (e.g., Streamlit, Gradio, or backend servers), Python won’t be able to access the user’s audio devices. In these cases, use our [TypeScript quickstart](https://dev.hume.ai/docs/speech-to-speech-evi/quickstart/typescript) to build a client-side component that connects to EVI from the browser.

1. [**Environment setup**](#env-setup): Download package and system dependencies to run EVI.
2. [**Import statements and helpers**](#import-statements-and-helpers): Import needed symbols and define helper functions.
3. [**Authentication**](#authentication): Use your API credentials to authenticate your EVI application.
4. [**Connection**](#connection): Set up a secure WebSocket connection to interact with EVI.
5. [**Handling incoming messages**](#incoming-messages): Process incoming chat events.
6. [**Audio input**](#audio-input): Capture audio data from an input device and send to EVI.

#### [Looking for sample code?](https://github.com/HumeAI/hume-api-examples/tree/main/evi/evi-python-quickstart)

See the complete implementation of this guide on GitHub

#### [Python SDK](https://github.com/HumeAI/hume-python-sdk)

Explore or contribute to Hume's Python SDK on GitHub

> **Tip**
>
> Hume's Python SDK supports EVI using Python versions `3.9` through `3.13` on macOS and Linux platforms. The
> `evi-python-quickstart` example project used in this guide requires Python `3.11+`
> (`requires-python = ">=3.11"`). The full SDK specification can be found in the
> [Python SDK's readme](https://github.com/HumeAI/hume-python-sdk).

## Environment setup \[#env-setup]

This guide uses the Hume SDK Python package `hume` with the `[microphone]` package extra. You can install this with `uv` (recommended), `poetry`, or `pip`. It also uses the `python-dotenv` package for loading environment variables from an `.env` file.

#### uv

```shell
uv init
uv add hume[microphone] python-dotenv
```

#### poetry

```shell
poetry add hume[microphone] python-dotenv
```

#### pip

```shell
pip install hume[microphone] python-dotenv
```

### System dependencies

The Hume Python SDK uses the [sounddevice](https://python-sounddevice.readthedocs.io/en/0.5.1/installation.html) library for audio recording and playback, which relies on the [PortAudio C Library](https://www.portaudio.com/) to be installed on your system. On macOS and Windows, PortAudio is typically included with the `sounddevice` package, so no additional installation is required. However, on Linux, you will need to manually install `PortAudio` correctly for your distribution.

#### Installation on debian-based Linux systems

```bash
sudo apt-get --yes update
sudo apt-get --yes install libasound2-dev libportaudio2
```

## Import statements and helpers \[#import-statements-and-helpers]

First, we import needed symbols from the Python standard library and the Hume SDK, and define some helpers that are useful for printing readable output to the terminal.

#### quickstart.py

```python maxLines=0
import asyncio
import datetime
import os
from dotenv import load_dotenv
from hume import MicrophoneInterface, Stream
from hume.client import AsyncHumeClient
from hume.empathic_voice.types import SubscribeEvent

def extract_top_n_emotions(emotion_scores: dict, n: int) -> dict:
    sorted_emotions = sorted(emotion_scores.items(), key=lambda item: item[1], reverse=True)
    top_n_emotions = {emotion: score for emotion, score in sorted_emotions[:n]}
    return top_n_emotions

def print_emotions(emotion_scores: dict) -> None:
    print(' | '.join([f"{emotion} ({score:.2f})" for emotion, score in emotion_scores.items()]))

def log(text: str) -> None:
    now = datetime.datetime.now(tz=datetime.timezone.utc).strftime("%H:%M:%S")
    print(f"[{now}] {text}")
```

## Authentication \[#authentication]

Log into your [Hume AI Account](https://app.hume.ai/keys) and [obtain an API key](/docs/introduction/api-key). Store it as `HUME_API_KEY` inside your project's `.env` file.

Read `HUME_API_KEY` and use it to instantiate the `AsyncHumeClient` class. This is the main entry point provided by the Hume Python SDK.

You can specify EVI's voice and behavior for a chat by [Creating a Configuration](/docs/speech-to-speech-evi/configuration/build-a-configuration) through the API or the [Hume platform web interface](https://app.hume.ai/evi/configs). Set `HUME_CONFIG_ID` in `.env` or as an environment variable and read it.

#### quickstart.py

```python maxLines=0
async def main() -> None:
    load_dotenv()
    HUME_API_KEY = os.getenv("HUME_API_KEY")
    HUME_CONFIG_ID = os.getenv("HUME_CONFIG_ID")
    client = AsyncHumeClient(api_key=HUME_API_KEY)
    ...
```

## Connection \[#connection]

To connect to an EVI chat, use `client.empathic_voice.chat.connect(...)` with connection arguments passed directly, such
as `config_id=HUME_CONFIG_ID`. EVI chats are event-based, so iterate over incoming events from the chat socket and
handle each message in your loop (or delegate to a helper function such as `on_message`).

#### quickstart.py

```python maxLines=0 highlight={3,6-13}
async def main() -> None:
    ...
    async def on_message(message: SubscribeEvent):
        # (Completed in later steps)
        ...
    async with client.empathic_voice.chat.connect(
        config_id=HUME_CONFIG_ID
    ) as socket:
        print("WebSocket connection opened.")
        async for message in socket:
            await on_message(message)
```

## Handling incoming messages \[#incoming-messages]

After you successfully connect to an EVI chat, iterate over messages from the socket and pass each one to your
`on_message` handler. These are described by the Hume SDK's `SubscribeEvent` type.

In this quickstart, the message handler logs `chat_metadata`, logs final `user_message` content and top emotions, and
raises an exception if an `error` event is received. Assistant output and other event types are ignored in this
handler.

#### quickstart.py

```python maxLines=0 highlight={2-20}
async def main() -> None:
    ...
    async def on_message(message: SubscribeEvent):
        if message.type == "chat_metadata":
            log(
                f"<{message.type}> Chat ID: {message.chat_id}, Chat Group ID: {message.chat_group_id}"
            )
        elif message.type == "user_message":
            if getattr(message, "interim", False):
                return
            log(f"{message.message.role}: {message.message.content}")
            print_emotions(
                extract_top_n_emotions(dict(message.models.prosody and message.models.prosody.scores or {}), 3)
            )
        elif message.type == "error":
            raise RuntimeError(
                f"Received error message from Hume websocket ({message.code}): {message.message}"
            )
        else:
            # Ignore assistant output and any other message types.
            return
    ...
```

## Audio input \[#audio-input]

The Hume SDK provides `MicrophoneInterface.start(...)`, which handles microphone capture and streaming recorded audio
through the WebSocket to EVI. `Stream.new()` is initialized and passed as `byte_stream` to
`MicrophoneInterface.start(...)`. This quickstart focuses on mic input and message handling; it does not include
manual `audio_output` playback queueing.

Pass the chat socket provided by `connect(...)` in order to use `MicrophoneInterface.start`:

#### quickstart.py

```python maxLines=0 highlight={8-12}
async def main() -> None:
    ...
    stream = Stream.new()
    ...
    async with client.empathic_voice.chat.connect(
        config_id=HUME_CONFIG_ID
    ) as socket:
        await MicrophoneInterface.start(
            socket,
            allow_user_interrupt=False,
            byte_stream=stream
        )
```

### Specify a microphone device

`MicrophoneInterface.start` will attempt to use the system's default audio input device. To specify a specific audio input device, you can pass it via the optional `device` parameter in `MicrophoneInterface.start`.

To view a list of available audio devices, run the following command:

#### List available audio devices

```bash
python -c "import sounddevice; print(sounddevice.query_devices())"
# outputs something like
  0 DELL U2720QM, Core Audio (0 in, 2 out)
  1 I, Phone 15 Pro Max Microphone, Core Audio (1 in, 0 out)
>  2 Studio Display Microphone, Core Audio (1 in, 0 out)
  3 Studio Display Speakers, Core Audio (0 in, 8 out)
  4 MacBook Pro Microphone, Core Audio (1 in, 0 out)
<  5 MacBook Pro Speakers, Core Audio (0 in, 2 out)
  6 Pro Tools Audio Bridge 16, Core Audio (16 in, 16 out)
  7 Pro Tools Audio Bridge 2-A, Core Audio (2 in, 2 out)
  8 Pro Tools Audio Bridge 2-B, Core Audio (2 in, 2 out)
  9 Pro Tools Audio Bridge 32, Core Audio (32 in, 32 out)
  10 Pro Tools Audio Bridge 64, Core Audio (64 in, 64 out)
  11 Pro Tools Audio Bridge 6, Core Audio (6 in, 6 out)
  12 Apowersoft Audio Device, Core Audio (2 in, 2 out)
  13 ZoomAudioDevice, Core Audio (2 in, 2 out)
```

If the `MacBook Pro Microphone` is the desired device, specify device 4 in the Microphone context. For example:

#### Python

```python maxLines=0 highlight={4}
# Specify device 4 in MicrophoneInterface 
await MicrophoneInterface.start(
  socket,
  device=4,
  allow_user_interrupt=False,
  byte_stream=stream
)
```

For troubleshooting faulty device detection - particularly with systems using ALSA, the Advanced Linux Sound
Architecture, the device may also be directly specified using the `sounddevice` library:

#### Setting default sounddevice library device

```python maxLines=0
# Directly import the sounddevice library
import sounddevice as sd

# Set the default device prior to scheduling audio input task
sd.default.device = 4
```

### Interruption

The `allow_user_interrupt` parameter on `MicrophoneInterface.start(...)` controls whether the user can send a
message while the assistant is speaking:

#### Allowing an interrupt

```python maxLines=0 highlight={4}
# Specify allowing interruption
await MicrophoneInterface.start(
  socket,
  allow_user_interrupt=True,
  byte_stream=stream
)
```

* `allow_user_interrupt=True`: Allows the user to send microphone input even when the assistant is speaking. This enables
  more fluid, overlapping conversation.
* `allow_user_interrupt=False`: Prevents the user from sending microphone input while the assistant is speaking, ensuring
  that the user does not interrupt the assistant. This is useful in scenarios where clear, uninterrupted
  communication is important.

## Put it all together

Finally, add the following code at the end of your script to run the main function:

#### quickstart.py

```python maxLines=0
if __name__ == "__main__":
    asyncio.run(main())
```

View the complete `quickstart.py` code on [GitHub](https://github.com/HumeAI/hume-api-examples/blob/main/evi/evi-python-quickstart/quickstart.py)

## Next steps

**Congratulations!** You’ve successfully implemented a real-time conversational application using Hume’s Empathic Voice
Interface (EVI).

Next, consider exploring these areas to enhance your EVI application:

#### [Configure EVI](/docs/speech-to-speech-evi/configuration/build-a-configuration)

See detailed instructions on how you can customize EVI for your application needs.

#### [Chat History](/docs/speech-to-speech-evi/features/chat-history)

Learn how you can access and manage conversation transcripts and expression measures.

> **Info**
>
> For further details and practical examples, explore the [API Reference](/reference/speech-to-speech-evi/chat) and our [Hume API Examples](https://github.com/HumeAI/hume-api-examples/tree/main/evi) on GitHub.

---