Getting Started

Quickstart

Start synthesising speech with the SonexLabs API in under 5 minutes.

1

Create an account

Go to sonexlabs.com and sign up. Once logged in, you will be placed in a workspace (tenant).

2

Generate an API key

In the sidebar, go to Configurations → Developer. Under the API Keys tab, click Create key, give it a name, and copy the key immediately; it is shown only once.

Important: Keys start with vsk_ and are scoped to your workspace. Store them in environment variables, never in source code.
3

Make your first request

Replace YOUR_API_KEY and REPLACE_WITH_VOICE_ID below. You can find voice IDs by calling GET /v1/voices or browsing the voices in your dashboard. Only text is required — voice_id, language, speed, output_format, and sample_rate are all optional.

curl --location 'https://api.sonexlabs.com/v1/speech' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "text": "Your premium of rupees twelve thousand five hundred is due on the fifteenth of September.",
    "voice_id": "REPLACE_WITH_VOICE_ID",
    "language": "auto",
    "speed": 1.0,
    "output_format": "wav",
    "sample_rate": 24000
  }'

language defaults to "auto" (auto-detected from the text) — a value other than "auto"is accepted but has no effect on pronunciation. speed accepts 0.5–2.0 (default 1.0); values outside that range are clamped, not rejected. output_format accepts wav, mp3, ogg, or mulaw (default wav) — mulaw is G.711 mu-law in a WAV container, for telephony bridges. sample_rate accepts 8000, 16000, 22050, 24000, 44100, or 48000 Hz (default 24000); 8000/16000 are the common choices for telephony and ASR pipelines. text is limited to 5,000 characters (~4-5 minutes of audio) on this endpoint — use /v1/speech/stream below for longer input.

A successful response returns raw audio bytes, with Content-Type matching output_formatand the character count billed in the X-Chars-Billed header. If voice_id isn't recognized, the request still succeeds using the default platform voice — check the X-Voice-Fallback response header ("true"/"false") and X-Voice-Fallback-Reason to detect this instead of a silent mismatch.

Prefer a GUI? Run every endpoint from our Postman collection instead — it's pre-filled with the same requests shown on this page.

4

Stream a response

POST /v1/speech/stream takes the exact same request body as /v1/speech, but streams audio back as chunked HTTP sentence-by-sentence instead of waiting for the full file — use this for longer scripts to reduce perceived latency.

curl --location 'https://api.sonexlabs.com/v1/speech/stream' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "text": "Your premium of rupees twelve thousand five hundred is due on the fifteenth of September.",
    "voice_id": "REPLACE_WITH_VOICE_ID",
    "language": "auto",
    "speed": 1.0,
    "sample_rate": 24000
  }'

output_format is not accepted here — streamed audio is always chunked WAV/PCM. sample_rate is honored (same allowed values as /v1/speech). text is limited to 20,000 characters (~15-20 minutes of audio) on this endpoint.

Billing: your full request is pre-authorized against your balance upfront (so you can't start a request you can't afford). Complete the stream and you're billed the full requested amount. Disconnect early and you're billed a bounded estimate of how much audio actually reached you before the drop — never the full request for a stream that only partially delivered. X-Chars-Billed shows the requested/authorized amount; the amount actually charged for an early-disconnected stream will be lower and shows up in your usage ledger.

5

List available voices

Fetch the voices available to your workspace. The id field is what you pass as voice_id.

curl https://api.sonexlabs.com/v1/voices \
  -H "Authorization: Bearer YOUR_API_KEY"
[
  {
    "id": "72ly9crx9v",
    "name": "Alok",
    "language": "Hindi",
    "languages": ["Hindi"],
    "gender": "male",
    "provider": "panini",
    "type": "platform",
    "preview_url": "https://...",
    "tags": ["Indian", "Natural, Professional"],
    "created_at": null
  }
]
6

Check your balance

Each synthesis call deducts from your TTS service credits first, then from your wallet balance. Check your remaining balance at any time:

curl https://api.sonexlabs.com/v1/balance \
  -H "Authorization: Bearer YOUR_API_KEY"
{
  "wallet": { "balance": 5.00, "currency": "USD" },
  "credits": [
    { "service": "tts", "available": 50000, "next_expiry": "2027-06-01T00:00:00Z" }
  ]
}
7

Clone a voice

POST /v1/voices/clone is a multipart/form-data request, not JSON. Upload the reference audio directly with the file field (recommended), or pass audio_url if it is already hosted somewhere; provide exactly one of the two. Minimum 10 seconds of clean audio, one speaker. There is no language field: Pāṇini auto-detects language at synthesis time, same as with platform voices.

curl -X POST https://api.sonexlabs.com/v1/voices/clone \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "name=My Custom Voice" \
  -F "file=@reference.wav"

Runs synchronously; the response is the ready-to-use voice, no polling required.

{
  "id": "v_xyz789",
  "name": "My Custom Voice",
  "language": null,
  "languages": [],
  "gender": null,
  "provider": "custom",
  "type": "custom",
  "preview_url": "https://storage.sonexlabs.com/voice-profiles/v_xyz789/ref_audio.wav",
  "tags": ["custom", "cloned"],
  "created_at": "2026-08-05T18:19:22.468Z"
}

type is "platform" for built-in SonexLabs voices and "custom" for voices your workspace cloned. Use it to tell the two apart when you list voices with GET /v1/voices.

To remove a cloned voice, call DELETE /v1/voices/{voice_id}:

curl --location --request DELETE 'https://api.sonexlabs.com/v1/voices/REPLACE_WITH_VOICE_ID'   --header 'Authorization: Bearer YOUR_API_KEY'

Supported languages

Pāṇini TTS supports 250+ languages and always auto-detects the language from your input text. No configuration needed. The optional language field on POST /v1/speech and POST /v1/speech/stream defaults to "auto"; leave it as "auto" to auto-detect.

🇬🇪 Abkhazian
🇿🇦 Afrikaans
🇦🇱 Albanian
🇩🇿 Algerian Arabic
🇨🇳 Amdo Tibetan
🇪🇹 Amharic
🇮🇳 Angami
🇮🇳 Angika
🇮🇳 Ao
🇦🇲 Armenian
🇮🇳 Assamese
🇪🇸 Asturian
🇹🇼 Atayal
🇮🇳 Awadhi
🇵🇪 Ayacucho Quechua
🇦🇿 Azerbaijani
🇧🇯 Baatonum
🇮🇳 Badaga
🇨🇲 Bafia
🇨🇲 Bafut
🇮🇳 Bagheli
🇮🇳 Bagri
🇨🇲 Bamun
🇮🇩 Banjar
🇨🇮 Baoulé
🇨🇲 Basa Cameroon
🇷🇺 Bashkir
🇪🇸 Basque
🇮🇳 Beary
🇧🇾 Belarusian
🇮🇳 Bengali
🇮🇳 Bhatri
🇮🇳 Bhili
🇮🇳 Bhojpuri
🇮🇳 Bishnupriya Manipuri
🇮🇳 Bodo
🇧🇦 Bosnian
🇮🇳 Braj
🇫🇷 Breton
🇮🇩 Buginese
🇧🇬 Bulgarian
🇮🇳 Bundeli
🇲🇲 Burmese
🇨🇲 Cameroon Pidgin
🇨🇳 Cantonese
🇪🇸 Catalan
🇵🇭 Cebuano
🇲🇽 Central Nahuatl
🇺🇸 Central Yupik
🇹🇩 Chadian Arabic
🇮🇳 Chakma
🇮🇳 Chhattisgarhi
🇲🇼 Chichewa
🇪🇨 Chimborazo Highland Quichua
🇨🇳 Chinese
🇷🇺 Chuvash
🇬🇧 Cornish
🇭🇷 Croatian
🇨🇿 Czech
🇬🇭 Dagbani
🇩🇰 Danish
🇮🇳 Deccani
🇮🇳 Dogri
🇳🇵 Dotyali
🇳🇱 Dutch
🇨🇮 Dyula
🇵🇰 Eastern Balochi
🇷🇺 Eastern Mari
🇪🇬 Egyptian Arabic
🇰🇪 Embu
🇬🇧 English
🇷🇺 Erzya
🌐 Esperanto
🇪🇪 Estonian
🇪🇸 Extremaduran
🇬🇭 Farefare
🇵🇭 Filipino
🇫🇮 Finnish
🇫🇷 French
🇬🇳 Fulah
🇪🇸 Galician
🇺🇬 Ganda
🇮🇳 Garhwali
🇮🇳 Garo
🇬🇪 Georgian
🇩🇪 German
🇮🇳 Goan Konkani
🇮🇳 Gondi
🇬🇷 Greek
🇵🇾 Guarani
🇮🇳 Gujarati
🇮🇳 Gujari
🇸🇦 Gulf Arabic
🇰🇪 Gusii
🇮🇳 Hadothi
🇭🇹 Haitian
🇮🇳 Haryanvi
🇳🇬 Hausa
🇺🇸 Hawaiian
🇮🇱 Hebrew
🇮🇳 Hindi
🇮🇳 Ho
🇲🇽 Huautla Mazatec
🇭🇺 Hungarian
🇮🇸 Icelandic
🇳🇬 Igbo
🇮🇩 Indonesian
🌐 Interlingua
🇺🇸 Inupiaq
🇮🇪 Irish
🇷🇺 Iron Ossetic
🇮🇳 Irula
🇮🇹 Italian
🇯🇵 Japanese
🇮🇩 Javanese
🇩🇿 Kabyle
🇰🇪 Kalenjin
🇰🇪 Kamba
🇮🇳 Kangri
🇮🇳 Kannada
🇮🇳 Kashmiri
🇰🇿 Kazakh
🇨🇳 Khams Tibetan
🇮🇳 Khasi
🇰🇭 Khmer
🇮🇳 Khortha
🇮🇳 Kinnauri
🇷🇼 Kinyarwanda
🇰🇬 Kirghiz
🇮🇳 Kodava
🇮🇳 Kokborok
🇮🇳 Konkani
🇮🇳 Konyak
🇰🇷 Korean
🇳🇦 Kuanyama
🇮🇳 Kui
🇮🇳 Kumaoni
🇮🇳 Kurumba
🇮🇳 Kutchi
🇮🇳 Lambadi
🇱🇦 Lao
🇱🇻 Latgalian
🇱🇻 Latvian
🇸🇾 Levantine Arabic
🇱🇾 Libyan Arabic
🇮🇹 Ligurian
🇨🇩 Lingala
🇱🇹 Lithuanian
🇮🇳 Lotha
🇨🇩 Luba-Lulua
🇰🇪 Luo
🇮🇳 Lushai
🇱🇺 Luxembourgish
🇲🇰 Macedo-Romanian
🇲🇰 Macedonian
🇮🇳 Magahi
🇮🇳 Maithili
🇲🇾 Malay
🇮🇳 Malayalam
🇮🇳 Malvi
🇮🇳 Manipuri
🇮🇲 Manx
🇮🇳 Mao
🇳🇿 Maori
🇮🇳 Marathi
🇮🇳 Marwari
🇰🇪 Meru
🇮🇶 Mesopotamian Arabic
🇮🇳 Mewari
🇨🇳 Min Nan Chinese
🇷🇺 Moksha
🇲🇳 Mongolian
🇲🇦 Moroccan Arabic
🇮🇳 Nagpuri
🇸🇦 Najdi Arabic
🇳🇦 Ndonga
🇮🇹 Neapolitan
🇳🇵 Nepali
🇳🇬 Nigerian Pidgin
🇮🇳 Nimadi
🇳🇴 Norwegian
🇫🇷 Occitan
🇮🇳 Odia
🇴🇲 Omani Arabic
🇪🇹 Oromo
🇮🇳 Pahari (Himachali)
🇵🇰 Pahari-Potwari
🇹🇼 Paiwan
🇮🇳 Panjabi
🇮🇳 Parkari Koli
🇮🇷 Persian
🇮🇹 Piemontese
🇲🇬 Plateau Malagasy
🇵🇱 Polish
🇵🇹 Portuguese
🇮🇳 Powari
🇮🇳 Rajbangsi
🇹🇿 Rangi
🇷🇴 Romanian
🇨🇭 Romansh
🇹🇿 Rombo
🇷🇺 Russian
🇹🇼 Sakizaya
🇮🇳 Sambalpuri
🇮🇳 Sanskrit
🇮🇳 Santali
🇵🇰 Saraiki
🇮🇹 Sardinian
🇮🇳 Saurashtra
🇮🇳 Savara
🇹🇼 Sediq
🇷🇸 Serbian
🇿🇼 Shona
🇮🇹 Sicilian
🇮🇳 Sikkimese
🇵🇰 Sindhi
🇱🇰 Sinhala
🇸🇰 Slovak
🇸🇮 Slovenian
🇸🇴 Somali
🇸🇳 Soninke
🇪🇸 Spanish
🇸🇦 Standard Arabic
🇸🇩 Sudanese Arabic
🇮🇳 Sumi
🇮🇳 Surgujia
🇰🇪 Swahili
🇸🇪 Swedish
🇮🇳 Sylheti
🇰🇪 Taita
🇹🇯 Tajik
🇮🇳 Tamil
🇮🇳 Tangkhul
🇷🇺 Tatar
🇮🇳 Telugu
🇹🇭 Thai
🇨🇳 Tibetan
🇪🇷 Tigrinya
🇺🇸 Tlingit
🌐 Toki Pona
🇧🇼 Tswana
🇮🇳 Tulu
🇹🇳 Tunisian Arabic
🇹🇷 Turkish
🇹🇲 Turkmen
🇬🇭 Twi
🇷🇺 Udmurt
🇨🇳 Uighur
🇺🇦 Ukrainian
🇦🇴 Umbundu
🇩🇪 Upper Sorbian
🇮🇳 Urdu
🇺🇿 Uzbek
🇻🇳 Vietnamese
🇪🇪 Võro
🇬🇧 Welsh
🇷🇺 Western Mari
🇸🇳 Wolof
🇿🇦 Xhosa
🇷🇺 Yakut
🇨🇲 Yangben
🇲🇽 Yaqui
🇳🇬 Yoruba
🇳🇪 Zarma
🇮🇳 Zeliangrong

Full list of 250+ languages at sonexlabs.com/models/tts.

Rate limits

The API enforces two limits per tenant, shared across all API keys in your workspace: 5 requests/second (burst) and 120 requests/minute (sustained). When exceeded, the API returns 429 Too Many Requests. Retry after the number of seconds indicated in the Retry-After response header.

Performance tips

Two client-side changes remove latency that has nothing to do with synthesis itself:

Reuse a persistent connection (keep-alive)

A fresh connection pays DNS lookup + TCP connect + TLS handshake before your request is even sent — measured at ~150-300ms combined. Reuse one HTTP client/session across requests (e.g. requests.Session() in Python, http.Agent({ keepAlive: true }) in Node, or any pooled aiohttp.ClientSession) instead of opening a new connection per call. This cost is paid once per connection, not once per request.

Choose a compressed output_format for slower links

output_format: "wav" is the default and is uncompressed — the response payload is larger and takes longer to download over higher-latency or bandwidth-constrained connections (e.g. mobile networks). If you don't need raw WAV, request "mp3" or "ogg"instead: same synthesis time on our side, smaller payload, faster download.

Error codes

401Invalid or missing API key.
402Insufficient balance. Top up in the dashboard.
422Validation error, check the request body.
429Rate limit exceeded. Back off and retry.
502TTS service temporarily unavailable.