Skip to Content
APIText to Speech

TTS API

Synthesize speech from text using Revolab’s Nada voice models.

POST https://api.revolab.ai/v1/tts

Request body (JSON)

ParameterTypeRequiredDefaultDescription
modelstringoptional"nada-1.0-flash"Model to use for synthesis. Values: nada-1.0-flash-lite, nada-1.0-flash, nada-1.0-pro
textstringrequiredText to synthesize. Maximum 500 characters.
voice_idstringrequiredVoice identifier. List the IDs your key accepts with GET /v1/voices.
languagestringoptional"en"BCP-47 language code (e.g., "en", "ms", "zh"). Defaults to English when omitted.
speedfloatoptionalnull (normal speed)Playback speed multiplier. Range: 0.5 (half speed) to 2.0 (double speed).
output_formatstringoptional"wav"Audio format of the output. Only WAV is supported in this release.
streambooleanoptionalfalsetrue streams raw PCM audio as it is synthesized instead of a buffered WAV — see Streaming.

Request examples

curl -X POST https://api.revolab.ai/v1/tts \ -H "Authorization: Bearer $REVOLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "nada-1.0-flash", "text": "The quick brown fox jumped over the lazy dog.", "voice_id": "<your-voice-id>", "language": "en", "speed": 1.0, "output_format": "wav" }'

Response (200 OK)

The response body is the audio — a complete WAV file (Content-Type: audio/wav). Save it or pipe it straight to a player; there is no download URL round-trip. Generation metrics ride as response headers:

HeaderDescription
X-Duration-SDuration of the generated audio in seconds.
X-Latency-MsSynthesis latency in milliseconds.
X-Request-IdRequest id — quote it in support requests.
curl -X POST https://api.revolab.ai/v1/tts \ -H "Authorization: Bearer $REVOLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "nada-1.0-flash", "voice_id": "<your-voice-id>", "text": "Hello!"}' \ -o hello.wav

Generations are still stored to your History (dashboard playback) — the upload happens in the background and never delays the response.

Streaming

Set "stream": true to receive audio while it is being synthesized, instead of waiting for the full generation. Playback can start after the first chunk — usually well under a second.

The streamed body is raw PCM: 24 kHz, mono, 16-bit little-endian (pcm_s16le). Raw PCM is used because WAV cannot stream — its header is length-prefixed. Two response headers describe the format so nothing is out-of-band:

HeaderValue
Content-Typeaudio/pcm
X-Sample-Rate24000
X-Audio-Formatpcm_s16le
curl --no-buffer -X POST https://api.revolab.ai/v1/tts \ -H "Authorization: Bearer $REVOLAB_API_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "nada-1.0-flash", "voice_id": "aisyah", "text": "Hello from Revolab!", "stream": true}' \ | ffplay -f s16le -ar 24000 -ch_layout mono -i - -autoexit -nodisp

Notes:

  • stream: true only supports the PCM stream — combining it with a non-default output_format returns 400 validation_error.
  • Streamed generations are not stored; the stream is the delivery. Use the default buffered response when you want the generation kept in your History.
  • Errors that occur before the first chunk (unknown voice, depleted balance, rate limits) return the normal JSON error envelope. Once audio starts flowing, the connection simply ends on failure — treat an unexpectedly short stream as an incomplete generation.
  • Billing meters the audio actually delivered: disconnect early and you are charged only for what you received.

Using an OpenAI SDK instead? The OpenAI-compatible endpoint offers the same stream via response_format: "pcm" (and an SSE framing via stream_format: "sse").

Status codes

200 OK — Success

WAV audio bytes (audio/wav) with X-Duration-S / X-Latency-Ms headers.

400 Bad Request — Unknown model, text too long, invalid voice_id, or invalid output_format

{ "error": { "code": "validation_error", "message": "Unknown model 'nada-2.0'. Valid models: ['nada-1.0-flash', ...]", "request_id": "req_01HXYZ..." } }

401 Unauthorized — Missing, invalid, or revoked API key

{ "error": { "code": "unauthorized", "message": "Invalid API key format. Keys must start with 'rvl_live_'.", "request_id": "req_01HXYZ..." } }

422 Unprocessable Entity — Request body fails JSON schema validation (e.g., missing required field)

{ "detail": [ { "loc": ["body", "text"], "msg": "field required", "type": "value_error.missing" } ] }

402 Payment Required — The organization’s balance is depleted

{ "error": { "code": "insufficient_balance", "message": "Insufficient balance to complete this request.", "request_id": "req_01HXYZ..." } }

429 Too Many Requests — Rate limit exceeded

Also quota_exceeded (monthly cap) and concurrency_limit_exceeded (too many in-flight requests); honor Retry-After.

{ "error": { "code": "rate_limited", "message": "Rate limit exceeded.", "request_id": "req_01HXYZ..." } }

502 Bad Gateway — TTS model endpoint returned an error or malformed audio

{ "error": { "code": "endpoint_unavailable", "message": "The upstream TTS service returned an unexpected response.", "request_id": "req_01HXYZ..." } }

503 Service Unavailable — TTS service is under maintenance or endpoint config is missing

{ "error": { "code": "service_unavailable", "message": "TTS service is temporarily unavailable.", "request_id": "req_01HXYZ..." } }