Voice API
Use the OpenAI-compatible endpoint with any OpenAI SDK, add speech to a Pipecat or LiveKit voice agent, stream over a WebSocket, or transcribe recordings with batch speech to text. Get a key below, then follow the calls. Every account starts with free credits.
Using Claude, Cursor or VS Code? Connect our MCP server with one URL instead.
Get your API key
Sign in, create a key, and it works straight away.
Developer / API Access
Free credits. Every account gets a one-time grant of free credits (worth $0.10, about 10,000 characters of speech), shared across speech, transcription, dubbing, voice conversion and voice design. Long jobs use more credits than short ones. When they run out those tools return 402 until you add a payment method; after that you pay as you go. Accounts without a payment method can hold one active API key. The free credits are one capped pool per account, shared across all of your keys (creating or revoking a key does not reset it), and they are used up first. Voice cloning and music generation always need a payment method.
OpenAI-compatible API
POST https://api.readaloudai.org/v1/audio/speech accepts the OpenAI audio.speech.create request. OpenAI SDKs, Open WebUI, LiteLLM and other tools that take an OpenAI base URL work by changing only the base URL and the key. Auth is Authorization: Bearer <your key>, the same rtts_ key as the rest of this page.
The earlier names piper-default, piper and kokoro keep working as aliases.
Python (openai)
# pip install openai
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.readaloudai.org/v1",
api_key=os.environ["READALOUD_API_KEY"],
)
with client.audio.speech.with_streaming_response.create(
model="tts-1", # accepted and ignored; the voice decides
voice="readaloud-default", # OpenAI names such as "alloy" map to this
input="Hello from ReadAloud.",
response_format="mp3",
) as response:
response.stream_to_file("hello.mp3")JavaScript / TypeScript (openai)
// npm install openai
import fs from 'node:fs'
import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://api.readaloudai.org/v1',
apiKey: process.env.READALOUD_API_KEY,
})
const res = await client.audio.speech.create({
model: 'tts-1', // accepted and ignored; the voice decides
voice: 'readaloud-default', // OpenAI names such as 'alloy' map to this
input: 'Hello from ReadAloud.',
response_format: 'mp3',
})
fs.writeFileSync('hello.mp3', Buffer.from(await res.arrayBuffer()))curl
curl https://api.readaloudai.org/v1/audio/speech \
-H "Authorization: Bearer $READALOUD_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"tts-1","voice":"readaloud-default","input":"Hello from ReadAloud.","response_format":"mp3"}' \
--output hello.mp3Supported fields
| Field | Behaviour |
|---|---|
input | Required, up to 5,000 characters. |
voice | Use readaloud-default. The OpenAI stock names (alloy, ash, ballad, coral, echo, fable, nova, onyx, sage, shimmer, verse, marin, cedar) all map to readaloud-default, so they sound the same. Any other value is a ReadAloud voice id from GET /v1/voices, or custom:<id> for a voice you cloned (the German, Spanish, French, Italian and Brazilian Portuguese beta voices use this form too; see Languages below). An unknown voice returns 404. |
model | Accepted and ignored. The voice decides how the audio is made. |
response_format | mp3 (default, 24 kHz), opus (48 kHz, Ogg), wav (24 kHz) or pcm (24 kHz, 16-bit, mono). aac and flac return 400. |
speed | 0.25 to 4.0. |
instructions, stream_format | Accepted and ignored. |
Audio is streamed as it is produced, except wav, which is returned whole.
Not supported
aacandflacoutput (400).instructions(voice style prompts) is ignored.- The OpenAI Realtime API.
GET /v1/modelsin OpenAI’s shape. It returns a different list, so do not rely on it to discover models.
Errors, billing and limits
Errors use the OpenAI shape, so SDK error handling works unchanged:
{"error":{"message":"response_format must be one of: mp3, opus, wav, pcm (aac and flac are not supported)","type":"invalid_request_error","param":"response_format","code":"invalid_value"}}400a missing or invalid field, an unsupportedresponse_format, aspeedoutside 0.25 to 4.0, or input over 5,000 characters.401the key is missing, invalid or revoked.402the free allowance is used up. Add a payment method.404unknown voice.429the service is at capacity. Retry with a short backoff.
Billing, the free allowance and concurrency limits are the same as for every other text-to-speech route on this page.
Integrations
Plugins and packages for voice-agent frameworks and automation tools. The Pipecat and LiveKit plugins are written and maintained by ReadAloud, are MIT licensed, and read your key from READALOUD_API_KEY. They can output 8 kHz audio for phone calls, and interrupting the agent stops generation.
Pipecat
Package pipecat-readaloud (source). Requires pipecat-ai 1.12 or newer and Python 3.10+.
pip install pipecat-readaloud
export READALOUD_API_KEY=rtts_...import os
from pipecat_readaloud import ReadAloudHttpTTSService
tts = ReadAloudHttpTTSService(
api_key=os.environ["READALOUD_API_KEY"],
sample_rate=8000, # 8000 for Twilio / Telnyx, 24000 native, 16000 also works
settings=ReadAloudHttpTTSService.Settings(voice="readaloud-default", speed=1.0),
)
# then use it in your Pipeline: [..., llm, tts, transport.output(), ...]LiveKit Agents
Package livekit-plugins-readaloud (source). Requires livekit-agents 1.8 or newer and Python 3.10+.
pip install livekit-plugins-readaloud
export READALOUD_API_KEY=rtts_...from livekit.agents import AgentSession
from livekit.plugins import readaloud
session = AgentSession(
# stt=..., llm=...,
tts=readaloud.TTS(voice="readaloud-default", sample_rate=24000), # 24000 | 16000 | 8000
)Vapi
Vapi can call ReadAloud as a custom voice at POST https://api.readaloudai.org/v1/vapi/custom-voice. We have verified the endpoint directly: it returns raw 16-bit mono PCM (application/octet-stream) at 8000, 16000 and 24000 Hz and accepts your key as the x-vapi-secret header. On 2026-10-10 we also ran one real Vapi assistant call that used it as a custom voice with the inline secret: the call lasted 24 seconds, the assistant spoke five lines in the ReadAloud voice, and Vapi reported custom-voice latency of 266 ms and 493 ms on the two turns it measured. We have not tested the Custom Credential flow, the sample rate Vapi requested, how Vapi handles 429 responses and timeouts, or the audio quality by ear, so treat the Vapi setup as lightly tested. See the Vapi integration page for what was tested and what was not.
curl -X POST 'https://api.readaloudai.org/v1/vapi/custom-voice?voice=readaloud-default' \
-H "x-vapi-secret: $READALOUD_API_KEY" -H 'Content-Type: application/json' \
-d '{"message":{"type":"voice-request","text":"Hello from ReadAloud.","sampleRate":16000,"timestamp":1}}' \
-o out.pcmn8n
Community node n8n-nodes-readaloud (source). It is a community node, not an n8n-verified one: version 0.1.2 is in n8n’s verification review. In a self-hosted n8n, open Settings, Community Nodes, Install and enter the package name below, then add a ReadAloud API credential with your key. We installed it into a local n8n 2.42.6 and ran one workflow against the live API; n8n Cloud was not tested. See the n8n integration page for what was tested and what was not.
n8n-nodes-readaloudZapier
ReadAloud is in the Zapier app directory (source), currently in Zapier’s beta period. It has one action, Convert Text to Speech, and no triggers. Add the action to a Zap, connect with your API key, and set the text. The step returns a link to an MP3, WAV or Opus file, along with the file name, voice, format, character count and size. Text is limited to 5,000 characters per step. Zapier reviewed and approved the app on 2026-10-14.
Vercel AI SDK
Package ai-sdk-provider-readaloud adds ReadAloud to generateSpeech in AI SDK 7. We ran it with ai 7.0.137 against the live API. See the Vercel AI SDK integration page for what was tested and what was not.
npm install ai ai-sdk-provider-readaloud
export READALOUD_API_KEY=rtts_...Reference
Authorize
POST https://api.readaloudai.org/tts/authorize with JSON { "key", "engine" }. engine is "live" (ReadAloud Live) or "studio" (ReadAloud Studio) and defaults to ReadAloud Studio. Returns { token, url }. The token lasts 60 seconds, so authorize again for each new connection.
401the key is invalid or revoked.402the free characters are used up. Add a payment method in your dashboard.400unknown engine.
Connect
Open wss://…/tts?token=<token> at the URL from authorize. Keep the connection open across requests. Audio is PCM16 little-endian, mono, 24 kHz.
Messages you send
{ "type": "synthesize", "text": "…", "voice": "default", "speed": 1.0 }speaks the text. ReadAloud Live accepts up to 5,000 characters per request.{ "type": "stop" }ends the current speech at once. Use it when the caller interrupts.
Messages you receive
chunk_metaa JSON frame, immediately followed by one binary audio frame, once per sentence. With the default ReadAloud Live setting below, the first sentence of a request may arrive as two chunks.donethe request finished.cancelledyou stopped it. Only finished requests are billed.errorwith amessage. The connection stays usable unless it is closed.
Audio formats
Add "format": "…" to a synthesize message (ReadAloud Live engine). pcm_24000 is the default. For phone lines use mulaw_8000 or alaw_8000 (G.711, 8 kHz); pcm_8000 is also available. Each chunk_meta reports the format and sample_rate of the audio that follows. An unknown format returns an error and the connection stays open.
Faster first audio (ReadAloud Live)
ReadAloud Live cuts the first sentence at its first clause (comma, semicolon, colon or dash) and streams the first half sooner, so audio starts roughly 40–55% faster on longer first sentences. It is on by default. Add "first_chunk_split": false to a synthesize message or HTTP body to turn it off for that request. Only true and false are accepted, anything else is ignored. The SDKs expose this as firstChunkSplit (JavaScript) and first_chunk_split (Python).
HTTP streaming (ReadAloud Live)
POST to the http_url returned by authorize with Authorization: Bearer <token> and JSON { "text", "voice", "speed", "format" }. The response streams raw audio as each sentence is ready, with X-Sample-Rate and X-Audio-Format headers. At capacity you get 503 with Retry-After.
SDKs and MCP
The API is plain WebSocket and HTTP, so any client works; official client libraries are on PyPI and npm: pip install readaloud and npm install readaloud. Because a client takes your API key, use it from a server, not a browser. To use the voices from an AI assistant, see the MCP server.
Capacity and errors
Each ReadAloud Live server handles up to 12 simultaneous streams, and a second server starts automatically under load. Beyond that you get { "type": "error", "message": "at capacity, retry shortly" } and the socket closes with code 1013. Retry with a short backoff.
Languages (beta)
Besides English, ReadAloud Live has ten beta voices in German, Spanish, French, Italian and Brazilian Portuguese. They are ordinary voice ids: pass one as voice (OpenAI route) or in the path of POST /v1/text-to-speech/{voice_id}, and write the text in that language. GET /v1/voices lists them with language and gender.
| Language | Voices |
|---|---|
| German | custom:de-de-mls-f, custom:de-de-mls-m |
| Spanish | custom:es-pilot-f, custom:es-pilot-m |
| French | custom:fr-fr-mls-f, custom:fr-fr-mls-m |
| Italian | custom:it-it-serena, custom:it-it-paola (both female) |
| Portuguese (Brazil) | custom:pt-br-faber, custom:pt-br-cadu (both male) |
- Beta quality. Spanish is a pilot and can sound weak on very short sentences. German and French read at a slow, audiobook pace. Portuguese starts with a short lead-in silence on some voices.
- First use. Voices that are not kept warm can take 2 to 3 seconds on the first request, then run at normal speed.
- Model ids.
eleven_flash_v2_5andeleven_turbo_v2_5still list English only; a language voice is selected by its voice id. - Attribution. The German, French, Spanish and Italian (Serena) voices are trained on CC BY 4.0 data. If you publish audio, credit the data sets named in each voice's
descriptionfromGET /v1/voices.
Voice cloning
Create a custom voice from your own recordings and use it with custom:<id>. Training runs automatically on GPU and takes about 30–60 minutes.
- One-time charge per voice
- No extra cost to synthesize with it
- Same per-character rate as built-in voices
- Delete old ones to make room
- Rejected voices do not count
- 10–60 minutes of audio recommended
- Single speaker, clean recordings
- WAV, FLAC, or MP3 inside the zip
Requires a billing-enabled API key. Free-tier keys receive 402 Payment Required. The charge is created when you commit a dataset (step 4 below). Training starts automatically and you can poll until the voice is ready.
API_KEY="YOUR_API_KEY"
BASE="https://api.readaloudai.org"
# 1. Get the current consent text
curl -s "$BASE/v1/voices/enabled" \
-H "Authorization: Bearer $API_KEY"
# 2. Create a voice
curl -X POST "$BASE/v1/voices" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"speaker_name": "Jane Doe",
"attested_by": "Jane Doe",
"consent": true,
"consent_text_version": "2026-09-v1",
"consent_statement": "I am authorized to consent on behalf of the speaker named in this request, and that speaker has agreed to have their voice cloned and used to synthesize new speech through this service."
}'
# -> {"id": "v-a1b2c3d4e5"}
# 3. Upload recordings (single-shot zip)
curl -X PUT "$BASE/v1/voices/v-a1b2c3d4e5/dataset" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/zip" \
--data-binary @recordings.zip
# 4. Commit — starts training
curl -X POST "$BASE/v1/voices/v-a1b2c3d4e5/dataset/commit" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"parts": 1}'
# -> {"id": "v-a1b2c3d4e5", "status": "training", "clips": 42}Dataset guidelines
- One speaker only. Multiple speakers confuse the model.
- 10–60 minutes of audio is the sweet spot. More does not always help.
- Clean audio: minimal background noise, no music, no reverb.
- Natural speech: read or conversational, not whispered or shouted.
- Format: Pack WAV, FLAC, or MP3 files into a single zip. No nested folders needed.
- Consent required: You must have legal right to clone the voice. We record speaker name, attested-by, and a consent statement for every voice.
Speech to text (batch)
Transcribe a finished recording with ReadAloud speech to text. Try it in the browser at /transcribe, or with an API key through the MCP tool speech_to_text (up to 25 MB per call). This is batch only: you upload a file and get the whole transcript back. There is no live streaming transcription yet and no entity detection. Speaker labels are available with diarize=true (below).
1. Authorize. POST https://api.readaloudai.org/stt/authorize with JSON { "key" }. Returns { token, url }. It uses the same key, the same 401 and 402 errors, and the same 60 second token as the voice API. Authorize again if the token has expired before you upload. It returns 501 if speech to text is not enabled.
2. Upload. POST <url>/v1/stt with Authorization: Bearer <token> and the audio as the raw request body, or as a multipart/form-data upload in a file field. Parameters go in the query string (or as form fields):
formatauto(default, detected from the file),wav,flac,mp3,oggorm4a. For headerless phone audio usemulaw_8000oralaw_8000(G.711, 8 kHz mono) orpcm_16000(16-bit little-endian, 16 kHz mono).languageauto(default) lets speech to text detect the language. Or pass a two-letter code such asen. Many languages are supported. On clean read speech (FLEURS, 100 sentences per language, word error rate without number normalisation) we measured: Italian 2%, Spanish 4%, Portuguese 5%, French 5%, English 5.5%, Polish 6%, German 6%, Indonesian 6%, Turkish 6%, Dutch 7%, Russian 7% and Ukrainian 7%; Japanese 5% and Chinese 8% (character error); Vietnamese about 11%, Korean 13% and Arabic 18% (lower accuracy). Hindi is about 30% and works best withlanguage=hiset, because auto-detect labels some Hindi as Urdu. We do not claim Bengali. Auto-detection was correct on 100% of these clips (98% for Ukrainian). Accuracy on telephone audio, noise, accents and very short utterances will be lower. We measured English telephone audio and noise (see What we measured) but not telephone audio, noise or accents in other languages. In a separate check on eight languages, 0 of 240 clips of one second or longer came back empty, but 68 of 240 clips shorter than one second did (28%; mostly one-word cut-outs, where dropping the text is often right because the alternative is invented subtitle credits).word_timestampstrue(default) orfalse.diarizefalse(default) ortrueto label who is speaking. Each word gets an integerspeaker(0, 1, 2…, numbered in the order they first speak) and the response gains aspeakersblock:{ count, utterances: [{ speaker, start, end, text, first_word, last_word }], warnings }. The transcript text and word times are unchanged. Up to 8 speakers and 1 hour of audio per request. Not billed any differently.max_speakers(withdiarize=true) an integer from 1 to 8. If you know how many people are talking, set it: the smallest detected speakers are folded into the nearest remaining one until at most that many are left.speakers.warningsthen includesspeakers_mergedandspeakers.merged_speakerssays how many were folded in. A real extra speaker is lost by definition, so leave it unset when you do not know.keyterm(repeat it, one per term) names, products or jargon to get right. Up to 500 terms; about the 60 most relevant are used to bias recognition on each request and every term is checked afterwards, so longer lists work but help less.dict_modeoff(default) orshortlistfor dictionaries over 500 terms, up to 3,000. We transcribe once without the dictionary, choose the terms that sound like words actually spoken, and use only those. Send big lists as onekeyterms_jsonvalue instead of thousands ofkeytermparameters. English dictionaries only: for other languages the dictionary is ignored. Not available withguard=off.keyterms_json(withdict_mode=shortlist) a JSON array of strings or{ "term", "tier": 1|2|3, "alternatives": [...] }. Tier 1 terms win ties and keep their near-sound-alikes out of the prompt; tier 3 terms are only used to correct the transcript afterwards. Mark confusable names with a lower tier or list them as alternatives.
The response is JSON: { text, language, language_probability, duration, words: [{ word, start, end }], segments: [{ id, start, end, text }] }, with times in seconds. Long uploads may be answered with a 303 redirect while we work; your HTTP client has to follow it (curl -L; most libraries do).
How accurate are speaker labels? On 78 real two-party phone calls (about 3 hours, English, caller and callee on separate recorded channels), 99.0% of words were attributed to the correct side of the call, and the label never collapsed two people into one. The speaker count is less reliable: it was exactly 2 on 67% of those calls, and the rest were split into 3 or 4 speakers. If you know a call has two parties, treat extra speakers as splits of the two main ones, or set max_speakers=2, or record each side on its own channel and skip diarization. Accuracy drops with many speakers (above about 6), very short turns, overlapping speech and narrowband audio; warnings lists many_speakers, speaker_cap_reached (8), short_turns and telephony_audio when they apply. We have not measured meetings with many speakers or languages other than English. The first diarize request after a quiet period adds a few seconds while the model loads.
How well do long dictionaries work? On a deliberately hard synthetic test (600 spoken sentences full of look-alike names, English, most dictionary terms never spoken), the share of spoken terms found was 71% with 100 terms, 68% with 300, 66% with 1,000 and 64% with 3,000, against 45% with no dictionary. A long list does not perform like a short one. Marking your important terms as tier 1 raised their hit rate from 67% to 75% at 1,000 terms, at the cost of their look-alikes. We have not measured real contact lists or languages other than English.
How many terms should I send? Send the terms you actually expect in that recording, ideally 100 or fewer, and put the most important ones first (or mark them tier 1). More terms are not better: recognition of the right term drifts down as the list grows, as above. Terms that sound alike (Sushant and Sushma, Kubernetes and Kubeflow) cost accuracy, because when both are on the list and the audio fits either one we cannot tell which you meant. On short, easy lists the gain can be large and the risk is small: with 15 distinctive names and jargon terms, one speaker, 101 quiet clips, the share of those terms transcribed correctly went from 63% to 100% (19 occurrences, so treat it as an example, not a rate). On our mixed phone and noise test set a 40-term list barely moved term accuracy (87.5% to 89.6% of 48 occurrences), word error rate was unchanged overall (13.8% to 13.7%) and slightly worse on the real-phone clips (9.8% to 10.4%), so test a dictionary on your own audio before relying on it. In the long-dictionary test above, requests with a dictionary took roughly 0.7 to 1.2 seconds longer at the median than without one.
TOKEN=$(curl -s https://api.readaloudai.org/stt/authorize \
-H 'content-type: application/json' -d '{"key":"rtts_..."}' | jq -r .token)
# the response also has "url": use it in place of <url> below
curl -L -X POST "<url>/v1/stt?language=auto" \
-H "Authorization: Bearer $TOKEN" --data-binary @call.wav400unsupported or undecodable audio, an unknownformatorlanguage, or an empty body.401the token is missing, invalid or expired.413over the limits: 200 MB per upload and 3 hours of audio (1 hour withdiarize=true).502speaker labelling failed withdiarize=true. Retry, or retry withoutdiarize. Not billed.504the request took longer than 15 minutes in total. Split the file and retry.
Price: $0.11 per hour of audio, billed by the second on the audio’s full length (silence included) once the transcript is returned, with a 10 second minimum per request (so a 3 second clip is billed as 10 seconds, $0.0003). The minimum is billing only: the transcript and the returned duration are unchanged. Failed requests, and requests with no audio, are not billed. That is half of ElevenLabs Scribe’s batch list price of $0.22 per hour (as of 2026-10; its realtime price is $0.39 per hour). It uses the same free allowance as the voice API: 10,000 free characters is about 54 minutes of audio. On your invoice it appears as character equivalents on the same meter (about 3 per second of audio).
What we measured. All of these are English, on test sets we assembled, with no vendor comparison. They are not a guarantee for your audio: record a few of your own calls and score them before you commit.
- Real narrowband phone-style audio. 296 clips of role-played customer calls recorded as real 8 kHz telephone-bandwidth audio. Word error rate 9.8% (10.4% with a 40-term dictionary), and 90.2% of spoken digit sequences were exactly right (89.7% with the dictionary). Scoring ignores filler words and number formatting. The files are stored at 16 kHz and we do not know the original phone codec, the calls are role-played rather than live customer calls, and this is a single test set.
- Noise is our weaker case. With background noise mixed in at 10 dB (578 clips, noise recordings from a public sound library mixed in by us) the word error rate was 17.8% and digit sequences were 84.6% right (17.0% and 86.2% with a dictionary). The same test set mixes clean, simulated-telephone, real-telephone and noisy clips; over all 1,156 items the word error rate was 13.8%.
- Clean read speech. On 100 short clips of read English speech (LibriTTS-R, about 9 minutes, roughly 1,500 words) word error rate was 2.6% on clean audio and 2.8% on the same clips simulated as 8 kHz telephone audio. That is studio-quality read speech with no background noise or real codec damage, so it is the best case, not what to expect on calls.
- Known failure modes. In the test set above 0.3% of clips (4 of 1,156) came back with no text at all. On recordings with no speech, text was invented on about 6% of 184 English noise, music and call-line-gap clips (2026-10-05; a later change to the filter did not alter this within noise), and on 3 to 11% of non-speech clips across the language check. Check the output when the audio may be silent.
We have not measured live customer calls, accents, far-field microphones or real background noise, and we measured other languages only on clean read speech (see the language list above). The first request after a quiet period can take about ten extra seconds while a GPU starts.
Cold starts and timeouts. The first request after a quiet period can take 10 to 18 seconds while the GPU starts. Use a request timeout of at least 60 seconds, and retry once on a timeout or a 5xx response. After that, a short clip takes about a second. The web transcribe page pre-warms the worker when you open it; pre-warming for MCP sessions is planned but not live yet, so MCP clients should apply the same timeout and one retry.
Dubbing
Upload a recording in one language and get back a version spoken in another, with each translated segment timed to roughly match the original. Pipeline: transcribe → translate segment-by-segment (Claude) → resynthesize each segment, speeding up or slowing down (0.5×–2×) so it fits the original segment’s timing. Try it in the browser at /dub.
Credentials. The REST endpoints below (dubbing, voice conversion, voice design and audiobooks) accept either a signed-in session token (Authorization: Bearer <session token>) or an API key that our own MCP server passes on for you. They do not accept your rtts_ key directly: the key/authorize flow used elsewhere on this page does not apply here, and there is no way to send the key straight to these paths. To use an API key, call the matching MCP tool (dub_audio, convert_voice, design_voice, the audiobook tools). Through MCP, the work is billed to the account the key belongs to. Keys you create in the console above belong to your account; a key that is not tied to an account gets 402 from dubbing, voice design and voice conversion. Free credits are per account, so they follow the account, not the key. Speech to text and voice cloning have their own key-based routes, described in their sections.
1. Submit. POST https://api.readaloudai.org/api/dub,Authorization: Bearer <your session token> (or use the MCP tool dub_audio with an API key; the tool takes clips up to 25 MB),multipart/form-data with an audio file (wav, flac, ogg, mp3, mp4, m4a or webm, up to 50 MB) and body fields target_language (required), source_language (optional, auto-detected otherwise) and voice_id (optional). Returns 202 with { job_id, status: "processing" }.
2. Poll. GET /api/dub/:job_id returns { status, audio_url, segments: [{ source_text, translated_text, start_sec, end_sec, speed_used }], error } once status is ready or failed. The result is a single WAV file. The MCP equivalent is get_dub_status.
400missing/oversized/unsupported audio, or a missingtarget_language.402your free credits are used up. Add a payment method.404unknown job, or a job that belongs to someone else.
Price: $0.15 per minute of source audio, billed by the second rounded up, with a 30 second minimum ($0.075), once the job completes. Failed jobs are not billed. On your invoice it appears as character equivalents on the same meter as text-to-speech (250 per second of audio; 15,000 per minute).
Known limitations. Audio only — no video muxing or subtitle burn-in. Segment timing is a v1 approximation (whole-segment speed scaling, not real phoneme-level alignment), so lip-sync-grade timing shouldn’t be expected. Jobs run in-memory on a single instance rather than a durable queue, so a deploy or restart while a job is in flight will lose it — resubmit if that happens. Each segment over 2,000 characters is truncated.
Sound effects
Sound effect generation is temporarily unavailable while we rework its cost. It will return here when it is ready.
Voice conversion & voice design
Voice conversion spins up your own on-demand GPU container so your audio never sits on a shared server, and re-sings/re-speaks a source recording in a target voice. Try it in the browser at /convert-voice. It accepts a session token, or an API key through the MCP tool convert_voice (see Credentials under dubbing above), which sets up your private converter on the first call (about 3 minutes, and it returns a temporary error until it is ready, so call it again).
Voice design. POST /api/voice-design generates a synthetic voice from a text description and a sample sentence (session token, or the MCP tools design_voice and get_voice_design). Try it at /design-voice. It uses your free credits, then needs a payment method.
1. Deploy your container. POST /api/voice-convert/deploy (same session-token auth). Check status with GET .../deploy, tear down with DELETE .../deploy when you’re done — you aren’t billed for idle deploy time, only completed jobs (see pricing below).
2. Submit a job. POST /api/voice-convert/conversions (multipart source + target files) — wav, flac, ogg, mp3, mp4 or m4a, up to 25 MB. It requires an exact consent_statement field confirming you have rights to the audio.
3. Poll and fetch. GET .../conversions/:id for status, then GET .../:id/audio for the result (raw WAV bytes).
400no deployment, missing/bad file, or missing/wrongconsent_statement.400withcode: "deployment_required": no converter yet.POST /api/voice-convert/deploy, pollGETuntilstatus: "ready"(about 3 minutes), then retry.401not signed in.402your free credits are used up. Add a payment method.409a deployment already exists.429over 10 requests/hour.503the GPU backend isn’t configured in this environment.
Price: voice conversion is $0.10 per minute of source audio, with a 45 second minimum ($0.075). It is billed by the second rounded up, only for completed jobs. On your invoice it appears as character equivalents on the same meter as text-to-speech (10,000 per minute). Voice design is billed per generated voice on its own meter.
Audiobooks
Turn a block of text or an EPUB into a chaptered audiobook: each chapter is synthesized as its own long-form job, then all chapters can be exported as one MP3 with embedded chapter markers. Try it in the browser at /audiobooks. Accepts a session token, or an API key through the MCP tools create_audiobook, get_audiobook_status and export_audiobook.
1. Create. POST /api/audiobooks with { title, voice_id, speed?, source_type: "text" | "epub", text? | epub_base64? }. Chapters are detected automatically and queued as individual TTS jobs.
2. Poll. GET /api/audiobooks/:id/status for per-chapter status and duration.
3. Export. Once every chapter is ready, POST /api/audiobooks/:id/export concatenates them into one MP3 with ID3 chapter markers and returns a signed download URL.
400a chapter isn’t ready yet, no chapters detected, or a chapter over your plan’s per-job character limit.404unknown audiobook, or one that belongs to someone else.
Price: billed the same way as regular long-form text-to-speech — per character on the standard meter, once per chapter. There is no separate audiobook charge.
Known limitation. The export step is newly enabled and has been reviewed but not yet exercised against a real multi-chapter book end-to-end in production — if a chapter-marker or concatenation edge case turns up, let us know.
Pricing and benchmarks
ReadAloud Live $0.004 and ReadAloud Studio $0.01 per 1,000 characters; speech to text $0.11 per hour of audio; dubbing $0.15 per audio minute and voice conversion $0.10 per audio minute, each billed by the second rounded up (with the minimums above); audiobooks bill per character like regular text-to-speech. Cloning a voice through the API costs $2.50 per voice when you commit the dataset. Audio-based usage appears on your invoice as character equivalents on the text-to-speech meter ($0.01 per 1,000). See engines and benchmarks for how we measured latency against ElevenLabs.
ReadAloud and ElevenLabs, as of 2026-10-10
Prices are ElevenLabs’ published pay-as-you-go API list prices for text to speech, read from its pricing page on 2026-10-10. Time to first audio is the median (p50) and 95th percentile (p95) in milliseconds, measured in San Jose with short call-centre sentences.
| Service | Per 1,000 characters | Per 1M characters | Warm connection, p50 / p95 | New connection, p50 / p95 |
|---|---|---|---|---|
ReadAloud, default voice (readaloud-default) | $0.004 | $4 | about 250 / 340-360 ms | about 290 / 370-440 ms |
| ElevenLabs Flash v2.5 | $0.04 | $40 | 166-174 / 190-220 ms | 200-280 / 410-490 ms |
| ElevenLabs v4 Turbo | $0.04 list | $40 list | 187-196 / 290-460 ms | about 222 / 250-370 ms |
| ElevenLabs v2 Multilingual | $0.08 | $80 | about 1,040 / 1,140 ms | about 1,090 / 1,220 ms |
| ElevenLabs v3 | $0.08 | $80 | not measured | |
| ElevenLabs v3 Conversational | $0.04 | $40 | not measured | |
| ElevenLabs v4 | $0.08 list | $80 list | not measured | |
- Price: at list prices ReadAloud is one tenth of ElevenLabs Flash and v4 Turbo ($4 against $40 per million characters) and one twentieth of v2 Multilingual, v3 and v4 ($4 against $80). ElevenLabs runs time-limited promotions that lower some of these prices; the figures here are list prices. ElevenLabs conversational agents list at $0.08 per call minute with the language model billed separately; ReadAloud bills text to speech by character only.
- Speed: ElevenLabs Flash was faster than ReadAloud in our runs, by roughly 80 ms at the median on a warm connection. We do not claim ReadAloud is faster than Flash. It was about four times faster than v2 Multilingual, the model most often chosen for quality.
- Quality: we do not claim the same voice quality as ElevenLabs, which has far more voices and languages. ReadAloud has one English voice for most uses, plus ten beta voices in German, Spanish, French, Italian and Brazilian Portuguese. We do not publish side-by-side audio; try the default voice on your own text.
- Capacity: each server accepts a limited number of simultaneous streams and more servers start under load. Past capacity you get the
at capacityerror described above; retry with backoff.
Method: two runs on 2026-10-10, 08:11 to 08:16 UTC (about 1 a.m. in San Jose, a quiet hour), from a throwaway machine in the Fly.io San Jose region, requests interleaved between services. Each run had 40 requests per service on a reused connection and 20 on a new connection, rotating through six sentences of 35 to 60 characters. Time is from sending the request to the first audio byte. ReadAloud was reached the way a customer reaches it: authorize (about 15 ms), then a direct WebSocket; the new-connection figure includes the connection handshake but not the authorize call. ElevenLabs was reached over its streaming HTTP API, pcm_24000 output, default settings, with our own account; its new-connection figure includes TCP and TLS setup. v2 Multilingual was measured in one run only. With 20 to 40 samples the 95th percentile is rough, and results change with the hour, the sentence and the connection. Prices exclude taxes and ElevenLabs plan allowances. Re-check ElevenLabs’ pricing page before you rely on these figures; ElevenLabs advertises lower model latency than the end-to-end figures here, which include the network.
// 1. Get a short-lived token (valid 60 s). engine: "live" or "studio".
const { token, url } = await fetch("https://api.readaloudai.org/tts/authorize", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key: "YOUR_API_KEY", engine: "live" }),
}).then((r) => r.json())
// 2. Connect straight to the voice server.
const ws = new WebSocket(`${url}?token=${token}`)
ws.binaryType = "arraybuffer"
// 3. Ask for speech. Audio starts arriving as the first sentence is ready.
ws.onopen = () => ws.send(JSON.stringify({
type: "synthesize", text: "Thanks for calling. How can I help?", voice: "default", speed: 1.0,
}))
ws.onmessage = (e) => {
if (typeof e.data === "string") {
const msg = JSON.parse(e.data) // chunk_meta | done | cancelled | error
if (msg.type === "done") console.log("finished")
} else {
play(e.data) // PCM16 little-endian, mono, 24 kHz
}
}
// Caller interrupts? Stop mid-sentence:
// ws.send(JSON.stringify({ type: "stop" }))