# Build a Text-to-Speech App with ElevenLabs and Python

> Source: <https://dev.to/voice_developer/build-a-text-to-speech-app-with-elevenlabs-and-python-kaa>
> Published: 2026-10-21 19:59:04+00:00

If you’ve ever built a chatbot, an accessibility feature, or a voice‑enabled game, you know that a natural‑sounding voice can turn a static experience into something engaging and human‑like. Modern TTS engines are no longer limited to robotic monotones – they can capture emotion, intonation, and even mimic a specific speaker’s style.

That’s where **ElevenLabs** shines. Their API delivers high‑quality neural voices that are easy to integrate, and the pricing model works well for both hobby projects and production apps. In this post we’ll walk through building a simple Python app that sends text to ElevenLabs, receives an audio file, and plays it back. By the end you’ll have a reusable script you can drop into any project.

| What you need | Why | 
|---|---|
| **Python 3.8+** | The example uses f‑strings and `asyncio` . | 
| **pip** | To install the required packages. | 
| **ElevenLabs API key** | You’ll need an account; sign up via the affiliate link: [https://try.elevenlabs.io/kr07zfuqn1bp](https://try.elevenlabs.io/kr07zfuqn1bp) | 
| **A speaker or headphones** | To hear the generated audio. | 

Keep this token secret – treat it like a password.

```
pip install httpx python-dotenv pydub
```

`.env` file.
`ffmpeg` installed on your system; on macOS you can use `brew install ffmpeg`, on Ubuntu `sudo apt-get install ffmpeg`.)
Create a `.env` file in your project root:

```
ELEVENLABS_API_KEY=your_api_key_here
```

Below is a minimal async function that talks to ElevenLabs. It accepts the text you want spoken, the voice ID (we’ll use the default “Rachel” for now), and returns the path to a saved MP3 file.

``` python
import os
import httpx
from pathlib import Path
from dotenv import load_dotenv

load_dotenv()
API_KEY = os.getenv("ELEVENLABS_API_KEY")
BASE_URL = "https://api.elevenlabs.io/v1"

async def synthesize(
    text: str,
    voice_id: str = "EXAVITQu4vr4xnSDxMaL",  # default voice (Rachel)
    output_path: Path = Path("output.mp3"),
) -> Path:
    url = f"{BASE_URL}/text-to-speech/{voice_id}"
    headers = {
        "xi-api-key": API_KEY,
        "Content-Type": "application/json",
    }
    payload = {
        "text": text,
        "model_id": "eleven_monolingual_v1",
        "voice_settings": {
            "stability": 0.75,
            "similarity_boost": 0.75,
        },
    }

    async with httpx.AsyncClient(timeout=30.0) as client:
        response = await client.post(url, json=payload, headers=headers)

    response.raise_for_status()
    # The API streams raw audio bytes (MP3)
    output_path.write_bytes(response.content)
    return output_path
```

**What’s happening?** 

`/text-to-speech/{voice_id}`.
`model_id` selects the neural model; `eleven_monolingual_v1` is the most balanced for English.
`stability` and `similarity_boost` let you tweak how consistent the voice sounds vs. how expressive it is.
Feel free to experiment – a higher `stability` makes the voice more monotone, while a higher `similarity_boost` pushes it toward the reference voice.

``` python
from pydub import AudioSegment
from pydub.playback import play

def play_audio(file_path: Path):
    audio = AudioSegment.from_file(file_path, format="mp3")
    play(audio)
python
import asyncio
from pathlib import Path

async def main():
    text = (
        "Hello, fellow developer! "
        "With ElevenLabs, you can turn any string into a natural‑sounding voice."
    )
    output_file = Path("speech.mp3")

    print("Generating speech…")
    await synthesize(text, output_path=output_file)
    print(f"Saved to {output_file}")

    print("Playing back…")
    play_audio(output_file)

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

Run the script:

```
python tts_demo.py
```

You should hear the phrase spoken in a clear, human‑like voice. 🎉

ElevenLabs also offers **voice cloning** – you can upload a few minutes of a speaker’s recordings and generate a custom voice ID. The workflow is:

`/voices/add` endpoint (or via the web UI).
`voice_id` once the model is ready.
`synthesize` function above.
Here’s a quick curl example for uploading a sample (replace `YOUR_API_KEY` and `sample.wav` with your values):

```
curl -X POST "https://api.elevenlabs.io/v1/voices/add" \
  -H "xi-api-key: YOUR_API_KEY" \
  -F "name=MyCustomVoice" \
  -F "files[]=@sample.wav"
```

The response contains:

```
{
  "voice_id": "abc123def456",
  "name": "MyCustomVoice",
  "preview_url": "https://..."
}
```

Now just pass `"abc123def456"` as the `voice_id` to `synthesize` and you’ll hear *your* voice reading the text. This is a game‑changer for personalized assistants, audiobooks, and dubbing.

ElevenLabs enforces a per‑minute request quota based on your plan. If you exceed it, you’ll receive a **429 Too Many Requests** response. A simple retry strategy can keep your app smooth:

``` python
import time

async def safe_synthesize(*args, **kwargs):
    retries = 3
    backoff = 2
    for attempt in range(retries):
        try:
            return await synthesize(*args, **kwargs)
        except httpx.HTTPStatusError as exc:
            if exc.response.status_code == 429 and attempt < retries - 1:
                wait = backoff ** attempt
                print(f"Rate limited, sleeping {wait}s…")
                time.sleep(wait)
                continue
            raise
```

Wrap your calls with `await safe_synthesize(...)` to automatically back‑off on throttling.

If you want to expose this TTS capability as a micro‑service, Flask or FastAPI work nicely:

``` python
from fastapi import FastAPI, Body
from fastapi.responses import FileResponse

app = FastAPI()

@app.post("/speak")
async def speak(text: str = Body(...)):
    out_file = await safe_synthesize(text)
    return FileResponse(out_file, media_type="audio/mpeg")
```

Deploy to **Render**, **Fly.io**, or any container platform, and you’ll have an HTTP endpoint that turns any string into speech on demand.

| Tip | Reason | 
|---|---|
| **Cache results** | Many apps reuse the same phrases (e.g., “Welcome back”). Store the MP3 locally to avoid unnecessary API calls. | 
| **Validate input length** | The free tier caps requests at 2 000 characters. Trim or split longer texts before sending. | 
| **Secure the API key** | Use secret management (AWS Secrets Manager, GCP Secret Manager) instead of plain `.env` in production. | 
| **Monitor usage** | ElevenLabs provides usage stats in the dashboard; set alerts if you approach your quota. | 

You now have a fully functional Python text‑to‑speech app powered by ElevenLabs. From a quick demo to a cloud‑hosted micro‑service or a custom voice clone, the API is flexible enough to fit most developer workflows.

Give it a spin, experiment with voice settings, and maybe even train a voice that sounds like you. The possibilities are only limited by your imagination (and the API quota).

**Ready to bring natural speech to your next project?** Try ElevenLabs today with the affiliate link: [https://try.elevenlabs.io/kr07zfuqn1bp](https://try.elevenlabs.io/kr07zfuqn1bp) – you’ll get instant access to the API and a generous free tier to start building right away. Happy coding!
