---
title: "Deploy paradee-tts-api"
description: "OpenAI-compatible TTS: 8 MB CPU model, 4 voices, timestamps"
category: "AI/ML"
url: https://railway.com/deploy/paradee-tts-api
---

# Deploy paradee-tts-api

OpenAI-compatible TTS: 8 MB CPU model, 4 voices, timestamps

**[Deploy paradee-tts-api on Railway](https://railway.com/template/paradee-tts-api)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/paradee-tts-api/manifest.json

- **Creator:** Nameless 's Projects
- **Category:** AI/ML

## Template content

### paradee-tts-api

- **Source:** https://github.com/lNamelessl/paradee-tts-api
- **Health check:** /health
- **Public domain:** Yes

## Documentation

**Paradee TTS API — an OpenAI-compatible `/v1/audio/speech` service on an 8 MB CPU text-to-speech model. One service, zero variables, no API keys, no GPU, no volume.**

[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/rzQwNE)

Paste the URL Railway gives you into any OpenAI SDK as `base_url` and call `audio.speech.create`. Four English voices, word-level timestamps, 24 kHz mono WAV, 8.5 MB of int8 ONNX baked into the image at build time with a sha256 check and a build-time gate that fails the build if the model cannot speak offline.

## What you get

- `POST /v1/audio/speech` — `{input, voice, response_format, speed, model?}` → 16-bit PCM WAV, 24 kHz, mono.
- `POST /v1/audio/speech/with-timestamps` — same input → base64 WAV plus `words: [{word,start,end}]`.
- `GET /voices`, `GET /v1/models` (`paradee-1.2`), `GET /health`, `GET /docs`.
- Voices: `af_heart`, `am_puck` (American), `bf_isabella`, `bm_fable` (British, misaki's British lexicon).

```python
from openai import OpenAI

client = OpenAI(base_url="https://your-service.up.railway.app/v1", api_key="not-needed")
resp = client.audio.speech.create(
    model="paradee-1.2",
    voice="af_heart",
    input="Paradee is a small voice that runs anywhere.",
    response_format="wav",
    speed=1.0,
)
open("speech.wav", "wb").write(resp.content)
```

## Measured on Railway (shared vCPU, single replica, 1 thread)

These numbers were measured against a deployed instance with `tests/battery.py`, not copied from the model's README. Railway's shared vCPU is much slower than a desktop, and request latency varies with host load — the ranges below are real measurements, spread over repeated runs.

| What | Measured on Railway |
|---|---|
| Short sentence (4.6 s of audio), 10 calls | p50 **2.1–3.9 s**, p95 2.2–7.1 s (implied **1.2–2.2× realtime**) |
| 1,500-character paragraph (102.6 s of audio) | 6.7–12.7 s wall (**8–15× realtime**) |
| Speed scaling | `speed=2.0` → 1.92× shorter; `speed=0.5` → 2.02× longer |
| RSS after boot (American lexicon warm) | **332 MB** |
| RSS after the first British request | **~505 MB** |
| RSS after a maximum-length (1,500-char) request | **~766–795 MB plateau**, stable across repeats (not unbounded) |
| Cold start | about a minute including the healthcheck window; a restart reaches `/health` in ~15–20 s |
| Image | 8.5 MB int8 ONNX, 4 voices, 1 CPU thread, no GPU |

Memory is the number to watch if you run on a small instance: the per-request working set for a 102-second utterance is larger than the boot footprint and the process does not give it back. Repeating the same long request does not raise it further. The British lexicon loads on the first British request (one-off ~2 s); `/health` reports which accents are resident.

## Disclosures worth reading before you deploy

- **Output is stochastic, not bit-exact.** The exported graph contains `RandomNormal` noise nodes (the decoder's breathiness). Identical requests give the identical audio *length* — the predicted durations are deterministic — but not identical samples; the measured run-to-run difference is ~0.8% of full scale. Do not build byte-level caching on top of it; cache by text + voice + speed with your own TTL.
- **English only.** Other languages are phonemized as if they were English.
- **WAV only, 24 kHz mono.** There is no mp3/opus/flac encoder in the image, and requesting one returns `400 unsupported_response_format` with the reason spelled out rather than a WAV labelled mp3.
- **Limits.** `input` is capped at 4,000 characters (`400 input_too_long`); sentences longer than 510 phonemes are split at the last space that fits, so very long unpunctuated text gains small pauses. `speed` is clamped to 0.5–2.0 and echoed in `X-Paradee-Speed` / `X-Paradee-Clamped`.
- **One request at a time.** The ONNX session is shared and guarded by a lock inside a single worker; concurrency scales by replicas, not threads.
- **Quality is below the teacher.** The author's 200-sentence evaluation puts `af_heart` at UTMOS 4.40 versus 4.52 for Kokoro-82M (`bf_isabella` 4.06, `am_puck` 4.32, `bm_fable` 4.18), with word error rate within a few tenths of a point. This is a small, fast model, not a drop-in Kokoro replacement.
- **Early release.** Paradee 1.2.x moves quickly (1.0 → 1.2.1 in a few weeks). The image pins the upstream git commit, the Hugging Face revision, and the weight sha256, so upgrades are deliberate.
- **No server-side URL fetching.** The API takes text only; there is no SSRF surface.

# Deploy and Host

Click Deploy. Railway creates one service from this repository, builds the Docker image (multi-stage `python:3.12-slim`, non-root uid 10001), bakes the weights in, runs the build-time speech gate, and starts `uvicorn` on the injected `PORT`. A public domain is provisioned automatically and `/health` gates traffic: the endpoint only returns 200 once the model has loaded and can actually speak.

There is nothing to fill in — **zero variables**. `PORT` is injected by Railway and every other setting is baked into the image.

After the deployment is green:

```bash
curl -X POST https://your-service.up.railway.app/v1/audio/speech \
  -H 'Content-Type: application/json' \
  -d '{"input":"Paradee is a small voice that runs anywhere.","voice":"af_heart","response_format":"wav"}' \
  --output speech.wav
```

## About Hosting

The service is a single stateless container. The model files live inside the image (`/app/models/paradee_int8.onnx` plus `config.json`, both sha256-verified at build time), so there is no volume, no object storage, no database and no outbound network access at runtime (`HF_HUB_OFFLINE=1`, no Hugging Face call on boot). That also means the deploy is reproducible: the same image always speaks with the same model.

Because inference is CPU-bound and serialized, vertical scaling helps more than horizontal concurrency tricks: choose more vCPU for lower latency, or more replicas for more parallel callers. The container is sized to run on a small instance; give it at least 1 GB of memory if you expect maximum-length paragraphs.

Restart policy is `ON_FAILURE` with 10 retries, and the healthcheck is `/health` with a 300 s window — the first boot after a cold image pull is the slow part, not the model load.

## Why Deploy

- **No API keys, no per-character bill.** The model is 8.5 MB of int8 ONNX; you pay for a small container instead of for tokens.
- **OpenAI-compatible by design.** Existing `openai` SDK code works by changing `base_url`; errors use OpenAI's envelope so SDK clients raise real `BadRequestError`s, and `/v1/models` lists `paradee-1.2`.
- **Timestamps included.** The `/with-timestamps` endpoint returns per-word start/end times from the model itself, which is what subtitling and word-highlighting need.
- **Verified, not just packaged.** The build fails if the model cannot load and speak with the hub disabled, if British and American phonemization stop differing, or if the WAV/timestamp contracts break — so a green deploy is a working voice.
- **No vendor benchmark inheritance.** The latency and memory numbers in this listing were measured on Railway's shared vCPU; the model author's ~62× figure comes from a different CPU and is not repeated here.

## Common Use Cases

- Narration and accessibility: read articles, dashboards or documents aloud on demand.
- Voice agents and IVR prototypes where a held-open connection to a paid TTS API is overkill.
- Subtitle and transcript timing: synthesize and get word-level timestamps in one call.
- Local-first and privacy-sensitive deployments: the text never leaves your Railway project and the process makes no outbound calls.
- Testing OpenAI-compatible clients without spending on a commercial TTS provider.

## Dependencies for

One Railway service from one public GitHub repository. No database, no cache, no volume, no credentials, no environment variables. The image already contains everything the model needs.

### Deployment Dependencies

- **Paradee 1.2** — model and code by Sahil Mahendrakar, [Apache-2.0](https://github.com/sahilmahendrakar/paradee), weights at [sahilmahendrakar/Paradee-8M-v1.2](https://huggingface.co/sahilmahendrakar/Paradee-8M-v1.2), pinned to git commit `c2789ef9abf54a9384eef89cc7de3f1afe5ad01d` (tag v1.2.1) and Hugging Face revision `ed2934d4df8a4fd891e29584c5e1104649409765`.
- **Kokoro-82M** — the teacher model Paradee is distilled from, by hexgrad, [Apache-2.0](https://huggingface.co/hexgrad/Kokoro-82M); Paradee reuses its vocabulary and voice names.
- **Training sentences** — WikiText-103, **CC BY-SA 3.0**.
- **misaki** — grapheme-to-phoneme (Kokoro's own G2P), by hexgrad; `en_core_web_sm` 3.8.0 (MIT) is installed at build time so no download happens at runtime.
- **eSpeak NG** — bundled inside the `espeakng-loader` wheel (GPL-3.0 library loaded at runtime as a separate work); no system package is needed.
- **This wrapper** — FastAPI + uvicorn on `python:3.12-slim` (Python 3.12 is required: misaki supports versions below 3.13), **Apache-2.0**.

## Similar templates

- [Chat Chat](https://railway.com/deploy/-WWW5r) — Chat Chat, your own unified chat and search to AI platform.
- [Weaviate](https://railway.com/deploy/weaviate-1) — Vector database with hybrid search and an API key-protected API
- [Möbius v0.1 Personal AI OS](https://railway.com/deploy/mobius-v01-personal-ai-os) — Self-hosted AI agent that builds and runs apps for you, with memory.

Open this page in a browser: https://railway.com/deploy/paradee-tts-api
