API reference — Text-to-Speech
API reference — Text-to-Speech
The contract for invoking a self-hosted Bulbul v3 endpoint. Send model: bulbul:v3 in every request — it is the only accepted value (bulbul:v3-beta and v2 are rejected). Request bodies are JSON; responses are base64 audio (real-time) or a raw audio byte stream (streaming).
Real-time — InvokeEndpoint
Request fields
The 38 voices are: aditya, ritu, priya, neha, rahul, pooja, rohan, simran, kavya, amit, dev, ishita, shreya, ratan, varun, manan, sumit, roopa, kabir, aayan, shubh, ashutosh, advait, anand, tanya, tarun, sunny, mani, gokul, vijay, shruti, suhani, mohit, kavitha, rehan, soham, rupali, niharika.
Response
audios always contains exactly one element (the list is a historical artifact).
Server-side streaming (SSE) — InvokeEndpointWithResponseStream
Call the streaming operation and set "stream": true in the request body. The flag lives in the JSON payload, not in an API parameter — SageMaker relays the body to the container verbatim, so calling the streaming API without the flag returns a single buffered chunk. The response is a sequence of PayloadPart events carrying raw encoded audio; concatenate them. Time-to-first-byte is ~0.5–0.6 s regardless of text length. Max text 3500 characters.
Bidirectional — InvokeEndpointWithBidirectionalStream
A full-duplex session over SigV4 HTTP/2, port 8443 (client aws-sdk-sagemaker-runtime-http2). Put model in the URL-encoded ModelQueryString. Frames are sent with data_type="UTF8":
- Send:
{"type":"config","data":{…speaker, language_code, output_audio_codec, speech_sample_rate, pace…}}, then per turn{"type":"text","data":{"text":"…"}}…{"type":"flush"};{"type":"ping"}keep-alive. - Receive:
{"type":"audio","data":{"audio":"<base64>"}},{"type":"event","data":{"event_type":"final"}}, and on failure{"type":"error","data":{"message":"…","code":<http status>}}.
Codec caveat. For lossy codecs (mp3 / aac / opus) the final partial frame plus the container trailer are flushed at connection close, not at the turn’s final event (~72 ms mp3 / ~128 ms aac / ~200 ms opus). WAV and LINEAR16 are byte-exact at every final. flac is accepted on real-time (200) but rejected (400) on the streaming paths.
Errors
See Error handling on SageMaker for the 424 collapse and the shared envelope. Bad input → 400 invalid_request_error (wrong model, empty or over-length text, unsupported speech_sample_rate, pace out of range, malformed JSON) with a descriptive Pydantic message:
Overload → 503 / code: "service_overloaded" with body retry_after (shed immediately, never queued):