Deploy a Text-to-Speech API with an Open-Source Model
Eli Mernit
To deploy a text-to-speech API, load an open-source TTS model once when a GPU container starts, wrap it in an HTTP endpoint that takes text and returns audio, and put authentication and autoscaling in front of it. On Beam that is one decorated Python function and beam deploy. The example below serves Kokoro-82M on an RTX 4090 and scales to zero between requests.
Run beam deploy app.py:speak, then call the URL it prints:
The response holds a link to a 24 kHz WAV file that stays valid for an hour.
What a text-to-speech API needs
A TTS API is a small web service around a speech model. The model is usually the easy part to find. Most of the work is in keeping it loaded, getting audio back to the caller in a usable form, and paying only for the time it generates speech.
A production setup needs:
- A model that fits your use. Fixed voices, cloned voices, or voices described in a prompt are different models with different licenses.
- System dependencies. Many TTS models phonemize text with
espeak-ng, a Linux package that pip doesn't install. - Load once, serve many. Loading weights on every request adds seconds to each call. Load them when the container starts.
- An audio delivery format. Return a file URL for long clips, raw bytes for short ones, or a stream when the listener should hear the first sentence before the last one is generated.
- Auth. Open-source TTS servers such as Kokoro-FastAPI ship without authentication, so a public port is an open, billable API.
- Scaling and idle cost. TTS traffic tends to come in bursts. A GPU billed around the clock costs more than the model ever will.
Which open-source TTS model to serve
Pick the model by how you need to control the voice. Kokoro-82M is the default for an API with fixed voices because it is small, fast, and Apache-2.0 licensed. Choose Chatterbox or Zonos when you need to clone a voice from a reference clip, and Parler-TTS when you want to describe the voice in plain text.
| Model | Size | Voice control | License | Best for |
|---|---|---|---|---|
| Kokoro-82M | 82M parameters | 54 built-in voices across 8 languages | Apache-2.0 | Fast, cheap fixed-voice APIs |
| Chatterbox | 0.5B parameters | Clone from a reference clip, 23 languages in the multilingual model | MIT | Cloned voices in many languages |
| Zonos v0.1 | About 2B parameters | Clone from a speaker sample, emotion controls | Apache-2.0 | Expressive 44 kHz output |
| Parler-TTS Mini v1 | 0.9B parameters | Text description of the speaker, 34 named voices | Apache-2.0 | Voices set by a written prompt |
Our roundup of open source text to speech models covers older options such as XTTS and MeloTTS, including their license limits. Deployment walkthroughs for two of the models above live in our Zonos TTS guide and Parler TTS inference guide. The serving pattern in this article works the same for all of them: swap the loader and the generate call.
How to deploy a TTS model on a serverless GPU
The deployment has four parts: an image with the model's dependencies, a loader that runs once per container, a handler that turns text into a file, and a deploy command. Each part maps to a few lines in the snippet above.
Build the image with espeak-ng
Kokoro uses the misaki library to convert text to phonemes and relies on espeak-ng as the fallback for words outside its dictionary, and Kokoro's own install instructions add it with apt. The image does the same alongside the Python packages. Zonos needs espeak-ng too, so the same line carries over if you switch models.
Load the model once with on_start
on_start=load_model runs when a container boots, and its return value is available to every request as context.on_start_value. KPipeline(lang_code="a") loads American English. Use "b" for British English, or another language code for the other six languages, and deploy one endpoint per language if you serve several.
The weights download from Hugging Face on the first cold start. For larger models, store them on a volume so later cold starts read from storage. Our guide to fast cold starts for serverless GPU inference shows the volume and caching setup.
Generate audio and return a URL
Kokoro's pipeline splits text on newlines by default and yields one result per segment, each with an audio tensor. The handler joins the segments, writes a WAV file with soundfile, and uploads it with Output, which returns a signed public URL. Returning a URL keeps the response small for long passages, and the caller can hand the link straight to a browser <audio> tag.
For short prompts you can skip the file and return the audio as base64 in the JSON body. Past a few hundred kilobytes, a URL is easier on clients.
Deploy and scale
beam deploy app.py:speak builds the image and prints the endpoint URL. Requests need your Beam token as a Bearer token by default, so the API isn't open to the internet.
QueueDepthAutoscaler(max_containers=5, tasks_per_container=1) starts another container when a request is waiting, up to five. keep_warm_seconds=60 keeps a container up for a minute after the last request so a burst of calls doesn't pay for repeated cold starts. Kokoro's weights are small next to the RTX 4090's 24 GB, so you can set workers=2 or more to run several copies per container, and raise tasks_per_container to match so the autoscaler fills each container before starting another.
How to stream TTS audio as it's generated
Stream the audio when a person is waiting to hear it, such as a voice assistant or a read-aloud button. Split the text into sentences, generate them one at a time, and send each chunk as soon as it's ready. The listener hears the first sentence while the rest is still being generated, instead of waiting for the whole passage.
Many TTS deployment examples, including Modal's Chatterbox example, generate the full clip and then send it. To stream, serve a FastAPI app with Beam's asgi decorator and return a StreamingResponse:
The split_pattern argument makes Kokoro yield one result per sentence instead of one per paragraph. Each chunk is raw 16-bit mono PCM at 24 kHz, which you can play as it arrives:
Browsers and telephony systems usually want a container format or a specific codec, so pipe the PCM through an encoder such as ffmpeg in the handler if your client can't take raw samples. Streaming shortens the time to first audio, but a cold container still has to boot and load the model first. A voice agent that needs speech in under a second should keep one container running with min_containers=1. For speech-to-text on the other end of the same conversation, our WhisperX tutorial covers serving transcription as an API.
Self-hosted TTS vs managed TTS APIs: cost
A self-hosted TTS API costs the GPU time it uses, not the characters it speaks. On Beam, the configuration above costs about $1.61 an hour while a container runs: $0.69 for the RTX 4090 plus 2 CPU cores and 8 GiB of RAM at the GPU-attached rates. It costs nothing while scaled to zero.
Managed APIs charge per character instead. A rough conversion is 1,000 characters to one minute of speech, which lets you compare the two directly: divide each provider's price for a minute of speech by Beam's per-second rate. If your container produces a minute of audio in less container time than that, self-hosting costs less.
| Provider and voice tier | Price per 1M characters | Price per minute of speech | Self-hosting is cheaper if a minute of audio takes less than |
|---|---|---|---|
| ElevenLabs Flash or Turbo | $40 | $0.040 | 90 seconds of container time |
| ElevenLabs Multilingual v2 or v3 | $80 | $0.080 | 179 seconds |
| OpenAI tts-1 | $15 | $0.015 | 34 seconds |
| Amazon Polly Neural | $16 | $0.016 | 36 seconds |
| DeepInfra hosted Kokoro-82M | $0.99 | $0.001 | 2 seconds |
Prices are the providers' published pay-as-you-go rates. Container time includes the keep_warm_seconds idle window after each burst, so spread it across the requests in that burst when you measure.
Two things follow from the table. Against premium voice APIs, a self-hosted small model wins on cost for almost any steady workload, so the real question is whether its voices are good enough for your product. Against a hosted copy of the same open model, self-hosting only wins at high, steady volume, or when you need something a shared API can't give you: a fine-tuned or cloned voice, a model it doesn't host, or text that can't leave your infrastructure.
Choosing where to host a TTS API
Choose by whether you need your own model or just good speech. Managed voice APIs are the fastest way to ship and lead on voice quality and cloning tools. A serverless GPU is the better fit when you want an open model you control, custom voices, or lower cost at volume, and you're willing to own about 50 lines of serving code.
- Beam fits teams that want the TTS endpoint written in Python next to the rest of their app, scale to zero on an RTX 4090, and the option to stream through FastAPI. You pay per millisecond of container time plus CPU and memory. Beam's serverless GPUs are the RTX 4090 and RTX 5090, which cover every model in the table above.
- ElevenLabs has the most polished voices and voice cloning, with monthly plans from $6. It is the most expensive per character in this comparison.
- OpenAI is the simplest if you already use its SDK.
tts-1costs $15 per million characters andtts-1-hdcosts $30, with a fixed set of voices. - Amazon Polly is cheap for standard voices at $4 per million characters and fits AWS-heavy stacks. Neural voices cost $16 and generative voices $30.
- DeepInfra hosts Kokoro-82M at $0.99 per million characters, which beats self-hosting the same model unless your traffic keeps a GPU busy most of the time.
- Modal is the closest DIY alternative: you write similar Python, and its example serves Chatterbox on an A10 at $0.000306 per second plus CPU and memory, with $30 a month in Starter credits.
If you're comparing hosts for other models too, our Replicate alternatives roundup covers the wider field, and the serverless GPU overview explains how billing and scale to zero work on Beam.
FAQ
What is the best open-source TTS model for an API?
Kokoro-82M is the best default for a production API with fixed voices. It has 82 million parameters, 54 voices in 8 languages, and an Apache-2.0 license, so it is cheap to run and safe to use commercially. Use Chatterbox (MIT) or Zonos (Apache-2.0) when you need voice cloning.
Can I run a TTS API on a CPU instead of a GPU?
Yes, small models like Kokoro and Piper run on CPUs, and some hosting templates are CPU-only. Generation is slower, which matters for long passages and for streaming to a waiting listener. Larger models such as Zonos and Chatterbox need a GPU for usable latency.
Is self-hosting TTS cheaper than ElevenLabs?
Usually, at steady volume. ElevenLabs Flash costs about $0.04 per minute of speech, so a self-hosted model on a $1.61-per-hour GPU container is cheaper whenever it produces a minute of audio in under about 90 seconds of container time. At low or very spiky volume, idle and cold-start time can erase the saving.
How do I stream audio from a TTS API?
Split the input into sentences, generate one sentence at a time, and send each audio chunk in a streaming HTTP response or over a WebSocket. With Kokoro, pass a sentence split_pattern to the pipeline and return a FastAPI StreamingResponse. The listener hears the first sentence before the rest has been generated.
How do I add voice cloning to a self-hosted TTS API?
Serve a model that supports cloning, such as Chatterbox or Zonos, and accept a reference audio clip in the request or store approved voice clips on a volume. Chatterbox takes the clip path through audio_prompt_path when generating. Get consent for any voice you clone.
How do I keep TTS latency low after the endpoint scales to zero?
Raise keep_warm_seconds so containers stay up between bursts, or set min_containers=1 on the autoscaler to keep one running all the time. Both bill while idle. Small models like Kokoro also cold start faster because there are fewer weights to download and load.
Ship your own text-to-speech API
Pick a model, load it in on_start, and deploy one function. You get an authenticated TTS endpoint on an RTX 4090 that scales with traffic and costs nothing when no one is calling it. Deploy your TTS API on Beam.


