POST /v1/tts POST¶
Synthesise text with voice and stream the audio back in format. The response starts after the first 80 ms frame is decoded.
POST /v1/tts HTTP/1.1
Host: api.vartalaap.si
x-api-key: vt_live_…
content-type: application/json
{"text": "आपकी EMI ₹12,450 है, जो 5 नवंबर को due है.", "voice": "anjura", "format": "wav"}
Request body¶
| Field | Type | Default | Notes |
|---|---|---|---|
text |
string | required | ≤ 4 000 characters after trimming. Run through the Hinglish normaliser unless raw is true. Must leave something pronounceable (else 400 nothing to say after normalisation). Splits into ≤ 64 sentence chunks of ≤ 50 tokens. |
voice |
string | anjura |
anjura or raj; GET /v1/voices lists what your key may use. Unknown → 400; not allowed for the key → 403. |
format |
string | wav |
wav · pcm16 (s16le mono 24 kHz) · opus (Ogg/Opus 48 kbit/s). Anything else → 400. |
stream |
bool | true |
true: chunked transfer, first bytes after the first frame; WAV header carries a placeholder length. false: buffered; WAV gets a correct length and Content-Length; Opus is a complete file. |
raw |
bool | false |
Skip the normaliser; the text must already be normalised. |
en_digits |
bool | false |
Read codes/OTPs/masked numbers with English digit names (four eight two nine) instead of Hindi. |
temperature |
number | 0.3 |
0–2. Sampling noise; lower is more stable, higher more varied. Out of range → 400. |
seed |
integer | random | Fixes the noise for reproducible output. |
request_id |
string | generated | Echoed as x-request-id, stored in your usage log. |
sample_rate |
integer | 24000 |
Only 24000 is accepted (400 otherwise). |
speed |
number | 1.0 |
Only 1.0 is accepted: the model has no rate control (400 otherwise). |
Response¶
200 OK, Content-Type audio/wav, audio/L16-style raw PCM (pcm16) or audio/ogg, x-request-id: <id>, Cache-Control: no-store.
With stream: true the body is chunked (Transfer-Encoding: chunked); with stream: false it has Content-Length.
Audio: 24 000 Hz, mono, 16-bit. Frames are 80 ms (1 920 samples); after the first frame they are decoded four at a time, so bytes arrive in ~320 ms steps ahead of real time.
Errors¶
| Status | Body | When |
|---|---|---|
400 |
{"error":"text is empty"} · unknown voice …; see /v1/voices · format must be pcm16, wav or opus · sample_rate must be 24000 · speed other than 1.0 is not supported by this model · temperature must be in [0, 2] · nothing to say after normalisation · text: … |
request validation |
401 |
missing x-api-key · invalid api key |
auth |
403 |
voice raj not allowed for this key |
key's voice allow-list |
413 |
text longer than 4000 characters · text splits into N chunks; max 64 |
size |
503 + Retry-After: 1 |
all workers busy and the queue is full; retry |
capacity (the contract) |
500 |
{"error":"<engine error>"} · worker gone |
engine failure; retry once |
Errors are decided before any audio is sent: a 200 that has started streaming will not turn into an error status. If the engine
fails mid-stream (rare), the body simply ends early; the WebSocket reports the same condition as an error event.
Examples¶
# streamed WAV straight to a player
curl -sN -X POST https://api.vartalaap.si/v1/tts -H "x-api-key: $VARTALAAP_API_KEY" -H 'content-type: application/json' \
-d '{"text":"नमस्ते, मैं सरल finserv से प्रिया बोल रही हूँ.","voice":"anjura","format":"wav"}' | ffplay -nodisp -autoexit -i -
# complete Ogg/Opus file with English digits for the code
curl -s -X POST https://api.vartalaap.si/v1/tts -H "x-api-key: $VARTALAAP_API_KEY" -H 'content-type: application/json' \
-d '{"text":"आपका OTP 482913 है.","voice":"raj","format":"opus","stream":false,"en_digits":true}' -o otp.ogg
# raw PCM into ffmpeg → m4a, reproducible with a seed
curl -sN -X POST https://api.vartalaap.si/v1/tts -H "x-api-key: $VARTALAAP_API_KEY" -H 'content-type: application/json' \
-d '{"text":"ठीक है, धन्यवाद.","format":"pcm16","seed":7}' | ffmpeg -f s16le -ar 24000 -ac 1 -i - -c:a aac -b:a 96k out.m4a
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": "आपकी EMI ₹12,450 है.", "voice": "anjura", "format": "pcm16"})
if r.status_code == 503:
wait = float(r.headers.get("retry-after", "1")) # nothing was billed; retry after `wait`
r.raise_for_status()
for pcm in r.iter_content(chunk_size=None):
sink.write(pcm) # s16le, 24 kHz, mono
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: "आपकी EMI ₹12,450 है.", voice: "anjura", format: "pcm16" }) });
if (res.status === 503) retryAfter(Number(res.headers.get("retry-after") || 1));
for await (const pcm of res.body) sink.write(pcm); // Buffer chunks, s16le 24 kHz mono