Skip to content

Vartalaap TTS · API documentation

Streaming Hinglish voices,
first audio in tens of milliseconds.

Hindi, English (US), English (IN) and the code-mixed Hinglish Indian customers actually speak. Streamed over REST or WebSocket from a plain API, with a normaliser that reads amounts, dates, OTPs and phone numbers the way a trained agent does. 10 paise per 1,000 characters.

41 msend-to-end time to first audio, warm, Mumbai
0.14–0.19real-time factor per stream
12.4×audio-seconds per wall-second, one 4-vCPU node
₹100per million characters, roughly 1,000 minutes of speech

Start in sixty seconds

tts.sh
export VARTALAAP_API_KEY="vt_live_…"   # from https://tts.vartalaap.si/keys
curl -sN -X POST https://api.vartalaap.si/v1/tts \
  -H "x-api-key: $VARTALAAP_API_KEY" -H "content-type: application/json" \
  -d '{"text":"Callback +44 00000 19024 पर करें।","voice":"saanvi","format":"wav"}' -o emi.wav
tts.py
import os, requests
r = requests.post("https://api.vartalaap.si/v1/tts", stream=True,
                  headers={"x-api-key": os.environ["VARTALAAP_API_KEY"]},
                  json={"text": "Callback +44 00000 19024 पर करें।", "voice": "saanvi", "format": "wav"})
r.raise_for_status()
with open("emi.wav", "wb") as f:
    for chunk in r.iter_content(chunk_size=None):   # bytes arrive as frames are decoded
        f.write(chunk)
tts.mjs
const res = await fetch("https://api.vartalaap.si/v1/tts", {
  method: "POST",
  headers: { "x-api-key": process.env.VARTALAAP_API_KEY, "content-type": "application/json" },
  body: JSON.stringify({ text: "Callback +44 00000 19024 पर करें।", voice: "saanvi", format: "wav" }),
});
await pipeline(Readable.fromWeb(res.body), createWriteStream("emi.wav"));
tts.py
import asyncio, json, os, websockets
async def main():
    async with websockets.connect("wss://api.vartalaap.si/v1/tts/stream",
                                  additional_headers={"x-api-key": os.environ["VARTALAAP_API_KEY"]}) as ws:
        await ws.send(json.dumps({"text": "Callback +44 00000 19024 पर करें।", "voice": "saanvi", "format": "pcm16"}))
        async for msg in ws:
            if isinstance(msg, bytes): play(msg)                       # 16-bit PCM, 24 kHz
            elif json.loads(msg)["type"] in ("end", "error"): break
asyncio.run(main())

The response is a WAV stream: the first bytes arrive after the first 80 ms frame is decoded, so a player can start before synthesis finishes. For conversations, open a WebSocket once and send sentences as your LLM produces them; the server tells you the exact moment of first audio.

Build with it

How the engine behaves

Normalises first. ₹12,450 → बारह हज़ार चार सौ पचास रुपये, EMI → E M I, OTP 482913 digit by digit. Preview it without synthesising.

Chunks on sentences, at most 50 tokens each, and streams chunk after chunk on one connection with a chunk_end event for each.

Caches text prefills per voice and chunk: a repeated IVR line starts in about 18 ms of engine time.

Says no cleanly. When every worker is busy you get 503 with Retry-After, nothing is billed, and you retry. Limits & errors.

Where things live

API https://api.vartalaap.si, REST and WebSocket, Mumbai
Developer portal tts.vartalaap.si: keys, playground with a live latency waterfall, usage, billing
Status tts.vartalaap.si/status, GET /readyz
Changelog changelog