Why a Python TTS App?
If you’ve ever wanted to turn a paragraph of text into a natural‑sounding voice clip, you’re not alone. Text‑to‑speech (TTS) has moved far beyond the robotic “read‑aloud” voices of the early 2000s. Today, you can generate expressive, human‑like speech in a matter of seconds, and you can even clone a specific voice with a few minutes of audio.
In this post we’ll walk through a complete, production‑ready Python app that talks back to you using ElevenLabs – a service that’s quickly become the go‑to for high‑quality voice AI. By the end you’ll have:
- A small Flask server that accepts raw text and returns an MP3 file
- A reusable helper that talks to the ElevenLabs API (both via
requestsandcurl) - A quick demo of voice cloning using a custom audio sample
Let’s dive in.
1. Get an ElevenLabs API key
First things first: you need an API key from ElevenLabs. Sign up at the affiliate link below – it’s free to start and gives you a generous quota for testing.
Once you have the key, store it safely. For local development a .env file works fine:
# .env
ELEVENLABS_API_KEY=your_secret_key_here
We’ll load this with python-dotenv later.
2. Set up the Python environment
Create a fresh virtual environment and install the required packages:
python -m venv venv
source venv/bin/activate # on Windows: venv\Scripts\activate
pip install flask requests python-dotenv
-
flask– tiny web framework for our API -
requests– simple HTTP client -
python-dotenv– loads the.envfile intoos.environ
3. The core helper: talking to ElevenLabs
ElevenLabs exposes a straightforward REST endpoint. Below is a minimal wrapper that sends text and receives an MP3 stream.
# tts.py
import os
import requests
from typing import Optional
ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY")
BASE_URL = "https://api.elevenlabs.io/v1"
def synthesize(
text: str,
voice_id: str = "EXAVITQu4vr4xnSDxMaL", # default “Rachel” voice
model_id: str = "eleven_multilingual_v2",
stability: float = 0.75,
similarity_boost: float = 0.85,
) -> bytes:
"""
Call ElevenLabs TTS endpoint and return raw MP3 bytes.
"""
url = f"{BASE_URL}/text-to-speech/{voice_id}"
headers = {
"xi-api-key": ELEVENLABS_API_KEY,
"Content-Type": "application/json",
}
payload = {
"text": text,
"model_id": model_id,
"voice_settings": {
"stability": stability,
"similarity_boost": similarity_boost,
},
}
response = requests.post(url, json=payload, headers=headers, timeout=30)
response.raise_for_status()
return response.content
A quick curl equivalent (useful for debugging) looks like this:
curl -X POST "https://api.elevenlabs.io/v1/text-to-speech/EXAVITQu4vr4xnSDxMaL" \
-H "xi-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "Hello, world! This is ElevenLabs speaking.",
"model_id": "eleven_multilingual_v2",
"voice_settings": {
"stability": 0.75,
"similarity_boost": 0.85
}
}' --output hello.mp3
Replace YOUR_API_KEY with the key you got from the link above.
4. Build a tiny Flask wrapper
Now let’s expose the TTS function as a web endpoint. This makes it easy to integrate with front‑ends, mobile apps, or even Discord bots.
# app.py
import os
from flask import Flask, request, send_file, abort
from io import BytesIO
from dotenv import load_dotenv
from tts import synthesize
load_dotenv() # pulls ELEVENLABS_API_KEY into the environment
app = Flask(__name__)
@app.route("/speak", methods=["POST"])
def speak():
data = request.get_json()
if not data or "text" not in data:
abort(400, description="JSON payload must contain a 'text' field")
text = data["text"]
voice_id = data.get("voice_id", "EXAVITQu4vr4xnSDxMaL")
try:
audio_bytes = synthesize(text, voice_id=voice_id)
except Exception as e:
abort(500, description=str(e))
# Return as an in‑memory MP3 file
return send_file(
BytesIO(audio_bytes),
mimetype="audio/mpeg",
as_attachment=False,
download_name="speech.mp3",
)
if __name__ == "__main__":
port = int(os.getenv("PORT", 5000))
app.run(host="0.0.0.0", port=port, debug=True)
Run it:
python app.py
Then test with curl:
curl -X POST http://localhost:5000/speak \
-H "Content-Type: application/json" \
-d '{"text":"ElevenLabs makes voice AI feel magical!"}' \
--output result.mp3
Open result.mp3 – you should hear a clear, natural voice speaking your sentence.
5. Voice cloning in a few lines
ElevenLabs also lets you clone a custom voice using a short audio sample (as little as 30 seconds). The workflow is:
- Upload a WAV/MP3 file to the
/voices/addendpoint. - Retrieve the new
voice_id. - Use that
voice_idin thesynthesizecall.
Here’s a helper that does the upload:
def add_voice(name: str, audio_path: str) -> str:
"""
Upload a voice sample and return the new voice_id.
"""
url = f"{BASE_URL}/voices/add"
headers = {"xi-api-key": ELEVENLABS_API_KEY}
files = {
"files": open(audio_path, "rb"),
"voice_name": (None, name),
}
response = requests.post(url, headers=headers, files=files)
response.raise_for_status()
voice_id = response.json()["voice_id"]
return voice_id
Example usage:
my_voice_id = add_voice("MyDemoVoice", "samples/my_demo.wav")
audio = synthesize("Hello, this is my own voice!", voice_id=my_voice_id)
with open("my_voice.mp3", "wb") as f:
f.write(audio)
Now you have a personalized voice that sounds like you (or anyone you have permission to clone). This is perfect for building audiobooks, interactive assistants, or even custom IVR systems.
6. Putting it together: a simple front‑end
If you want a quick UI to test the service, add an HTML page that posts to /speak and plays the result:
<!-- static/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ElevenLabs TTS Demo</title>
</head>
<body>
<h1>Text‑to‑Speech Playground</h1>
<textarea id="txt" rows="4" cols="50">Enter text here...</textarea><br>
<button onclick="speak()">Speak</button>
<audio id="player" controls></audio>
<script>
async function speak() {
const text = document.getElementById('txt').value;
const resp = await fetch('/speak', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({text})
});
const blob = await resp.blob();
const url = URL.createObjectURL(blob);
document.getElementById('player').src = url;
}
</script>
</body>
</html>
Serve the static folder from Flask:
app = Flask(__name__, static_folder="static")
# existing routes stay the same
Now you have a full‑stack demo that you can run locally, deploy to Render, Fly.io, or any container platform, and share with teammates.
7. Tips for Production
- Rate limiting – ElevenLabs enforces per‑key limits. Cache generated audio when possible.
- Error handling – The API may return 429 (Too Many Requests) or 422 (Invalid voice). Gracefully surface those messages to the client.
- Security – Never expose your API key to the browser. All calls to ElevenLabs must happen server‑side.
- Audio storage – For longer‑term use, consider uploading MP3s to S3 or another CDN and serving the URL instead of streaming from memory.
8. Next steps
- Multi‑language support – ElevenLabs models support dozens of languages; just pass the desired text.
-
Dynamic voice switching – Pull a list of available voices (
/voices) and let users pick. - Streaming – For real‑time chatbots, stream chunks of audio as they become available (requires WebSockets or Server‑Sent Events).
Ready to give it a spin?
All the heavy lifting of realistic speech synthesis and voice cloning is handled by ElevenLabs, letting you focus on the product logic. Grab your free API key, run the code above, and start building the next generation of voice‑first experiences.
👉 Try ElevenLabs now: https://try.elevenlabs.io/kr07zfuqn1bp
Happy coding!
Top comments (0)