Skip to content

Explore CallMissed

Text to speech API

Hindi that sounds like a person said it.

One POST sends text and a voice name, and audio bytes come straight back — no job id, no polling. bulbul:v3 covers 11 Indian language codes with 37 voices, and eight more models sit behind the same endpoint when you need another accent or another language.

Endpoint
POST /v1/audio/speech
Auth
Bearer cm_ key
bulbul:v3 voices
37
Per 10,000 characters
$0.3125

Build a request that the model will accept

Every model takes a different set of controls. Pick one below and the form offers only the fields the docs say it accepts, then writes the curl for you — so the command you paste into a terminal is valid before you run it.

$0.3125 per 10,000 characters

curl
curl -X POST https://api.callmissed.com/v1/audio/speech \
  -H "Authorization: Bearer cm_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bulbul:v3",
    "input": "Namaste, kaise hain aap?",
    "voice": "shubh",
    "language": "hi-IN",
    "response_format": "mp3"
  }' \
  --output speech.mp3

The response is the file

A successful call returns raw audio in the format you asked for, so you write the body to disk or pipe it to a player. There is no JSON envelope to unwrap and nothing to poll. These samples use plain HTTP; the OpenAI speech shape means client.audio.speech.create() works too once base_url points at https://api.callmissed.com/v1.

// Node 18+ — no SDK, just fetch
import { writeFile } from "node:fs/promises";

const res = await fetch("https://api.callmissed.com/v1/audio/speech", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.CALLMISSED_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "bulbul:v3",
    voice: "shubh",
    language: "hi-IN",
    input: "Namaste, kaise hain aap?",
    response_format: "mp3",
  }),
});
if (!res.ok) throw new Error("TTS failed: " + res.status);
await writeFile("speech.mp3", Buffer.from(await res.arrayBuffer()));

Streaming, where it exists

Set stream to true on deepgram-aura-2 or deepgram-aura-1 and audio arrives as a chunked HTTP response. Streaming carries raw encodings only — linear16, mulaw or alaw. Ask for mp3, opus, aac or flac with stream true and you get the full audio in one buffered response instead. Other models ignore the field.

curl -N -X POST https://api.callmissed.com/v1/audio/speech \
  -H "Authorization: Bearer cm_your_key" \
  -H "Content-Type: application/json" \
  -d '{"model": "deepgram-aura-2", "voice": "thalia", "input": "Streaming hello.", "response_format": "linear16", "stream": true}' \
  --output speech.raw
response_format
mp3opusaacflacwavpcm
speech_sample_rate
800016000220502400048000
8000 Hz comes out ready for a phone line, so telephony audio needs no resampling on your side.

Every voice name the reference publishes

Every bulbul:v3 voice speaks every one of its 11 language codes, so the voice name and the language field are independent choices. A voice name the model does not recognise falls back to its default rather than failing the request — pick a model below and click a name to copy it.

37 voices. Each speaks all 11 language codes — voice and language are independent fields.

The reference publishes no gender or per-language labels for the bulbul and gnani rosters, so none are claimed here.

bulbul:v3 language codes

  • hi-IN
  • bn-IN
  • ta-IN
  • te-IN
  • mr-IN
  • gu-IN
  • kn-IN
  • ml-IN
  • pa-IN
  • od-IN
  • en-IN

The sonic-3.6 library is bigger than any list

sonic-3.6 has 16 featured handles and a public library behind them, and it takes base codes like en and hi rather than hi-IN. Because the library is paginated and changes, ask the API for it instead of hardcoding names. riya, arushi, siya, parvati and kabir are native Hindi speakers — pair them with "language": "hi".

q
Free-text search over the voice library.
language
Filter to one base language code.
gender
masculine, feminine or gender_neutral.
limit
Page size, 1 to 100.
starting_after
Cursor for the next page.
# Search the sonic-3.6 voice library
curl "https://api.callmissed.com/v1/audio/voices?model=sonic-3.6&q=hindi&limit=50" \
  -H "Authorization: Bearer cm_your_key"

If the upstream library is briefly unreachable the first page still answers 200 with the featured handles and an extra "degraded": true field, so a voice picker keeps rendering and every voice it returns stays usable. Voice reference

Four ways to change how it lands

Text written for the eye reads badly out loud. These are the fields that fix it, and the models each one applies to — sending a control to a model that does not take it is the most common reason synthetic speech sounds wrong.

humanize

Every model. Default true.

Shapes your text for natural speech before synthesis: markdown and emoji are stripped, URLs and emails are spoken as words, long digit runs are grouped into readable chunks. Set it to false to synthesize your text byte for byte.

https://callmissed.com → "callmissed dot com"

temperature

bulbul:v3 only. 0.01 to 2.0, default 0.9.

The expressiveness dial. The default sits warmer than the model's own flat setting, so read-aloud copy already has some colour in it. Lower it for numbers and confirmations, raise it for conversational lines.

"temperature": 0.4 → flatter, steadier delivery

instructions

gpt-4o-mini-tts only. Up to 2,000 characters.

A plain-English brief for the delivery rather than a numeric dial. It describes how the line should land — pace, warmth, attitude — alongside the text you want spoken.

"instructions": "Calm, slow, reassuring."

Pause cues

deepgram-aura-2, deepgram-aura-1, aura-2-en, aura-2-es.

These four read punctuation you write into the text: an ellipsis gives a longer natural pause, a comma or period a short one, and um or uh render as natural hesitation. bulbul:v3 and gnani-timbre-v2.0 do not support pause markup or SSML — they would speak the dots aloud.

One moment... let me check that.

Nine models, one endpoint, one model field

Switching model is a one-word change. Prices are read from the same table billing uses, so this cannot drift. 4 of the 9 TTS models are on the free tier.

Text to speech models with voices, languages, speed range, expressiveness control, streaming and price
Model idVoicesLanguagesspeedExpressivenessStreamsPer 10K chars
bulbul:v3Free tier3711 Indian languages0.5–2temperature 0.01–2.0No$0.3125
sonic-3.616 featured + public library44, incl. Hindi and Hinglish0.6–1.5—No$0.5208
gnani-timbre-v2.073English, Hindi, other Indian0.85–1.15—No$0.2813
gpt-4o-mini-tts6—0.25–4instructions, up to 2000 charsNo$0.2083
deepgram-aura-290en, es, de, fr, nl, it, ja0.7–1.5pause cues in the textYes$0.3125
deepgram-aura-112English0.7–1.5pause cues in the textYes$0.1563
aura-2-enFree tier40English—pause cues in the textNo$0.4167
aura-2-esFree tier10Spanish—pause cues in the textNo$0.4167
melottsFree tier————No$0.05208

Browse the full model catalogue · See plans and credits · Go the other way with speech to text

Text to speech API questions

Which Indian languages does the text to speech API support?

bulbul:v3 speaks 11 Indian language codes: hi-IN, bn-IN, ta-IN, te-IN, mr-IN, gu-IN, kn-IN, ml-IN, pa-IN, od-IN and en-IN, with 37 voices. sonic-3.6 covers 44 languages including Hindi and Hinglish, and gnani-timbre-v2.0 has 73 voices across English, Hindi and other Indian languages.

How much does text to speech cost?

bulbul:v3 is $0.3125 per 10,000 characters. 4 of the 9 TTS models are on the free tier. Streaming is billed the same as non-streaming, per character. The model matrix on this page lists every model's price from the same table billing uses.

What audio formats can I get back?

Set response_format to mp3, opus, aac, flac, wav or pcm. speech_sample_rate accepts 8000, 16000, 22050, 24000 or 48000 Hz, so 8 kHz telephony audio needs no resampling on your side.

Can I stream audio as it is generated?

On deepgram-aura-2 and deepgram-aura-1, set stream to true and audio arrives as a chunked HTTP response. Streaming supports raw encodings only: linear16 (or pcm/wav), mulaw or alaw. If you ask for mp3, opus, aac or flac with stream true, the full audio is returned in one buffered response instead. Other models ignore stream.

How do I make the voice sound less robotic?

In order of impact: write text that reads like speech and leave humanize on (the default), which strips markdown and emoji and speaks URLs and long digit runs naturally. Then use the model's expressiveness control: temperature on bulbul:v3, instructions on gpt-4o-mini-tts. On the Aura models, write pauses into the text with an ellipsis. bulbul:v3 and gnani-timbre-v2.0 do not support pause markup or SSML.

Does the OpenAI SDK work?

Yes. The endpoint follows the OpenAI speech shape, so client.audio.speech.create() works once base_url is https://api.callmissed.com/v1 and the key is a cm_ API key. Plain HTTP works too, as the examples on this page show.