ElevenLabs Audio
| Method | Path | Use | Marketplace protocol |
|---|---|---|---|
POST | /v1/text-to-speech/{voice_id} | Synthesize speech and return the full audio | ElevenLabs Text to Speech |
POST | /v1/text-to-speech/{voice_id}/stream | Synthesize speech and stream audio as it is generated | ElevenLabs Text to Speech |
POST | /v1/text-to-speech/{voice_id}/with-timestamps | Synthesize speech with character-level timestamps | ElevenLabs Text to Speech with Timestamps |
POST | /v1/text-to-speech/{voice_id}/stream/with-timestamps | Stream audio together with timestamps | ElevenLabs Text to Speech with Timestamps |
GET | /v1/text-to-speech/{voice_id}/stream-input | WebSocket: send text incrementally, receive audio incrementally | ElevenLabs Text to Speech WebSocket |
Paths, request bodies, and responses follow ElevenLabs' official Text to Speech and stream-input docs. A model does not necessarily offer every endpoint: check the protocols shown in the model marketplace (last column above). Otherwise HTTP answers 400 model_protocol_not_offered and the WebSocket handshake answers 403.
HTTP speech synthesis
curl "https://api.modelgo.ai/v1/text-to-speech/JBFqnCBsd6RMkjVDRZzb?output_format=mp3_44100_128" \
-H "Authorization: Bearer $MODELGO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model_id": "<model>",
"text": "Hello from ModelGo.",
"voice_settings": { "stability": 0.5, "similarity_boost": 0.75 }
}' \
--output speech.mp3| Parameter | Where | Notes |
|---|---|---|
voice_id | Path | ElevenLabs voice ID |
output_format | Query | Output format such as mp3_44100_128 or pcm_16000; same values as the official API |
model_id | Body, required | Platform logical model name. ElevenLabs defaults it to eleven_multilingual_v2; here it must be set explicitly |
text | Body, required | Text to synthesize |
| Other official fields | Body | voice_settings, language_code, seed, previous_text, next_text, apply_text_normalization, …, forwarded as defined by ElevenLabs |
Query parameters such as optimize_streaming_latency and enable_logging are accepted but have no effect.
- The two endpoints without timestamps return audio bytes directly (for example
audio/mpeg);/streamsends them as they are generated. /with-timestampsreturns JSON: audio inaudio_base64(Base64), andalignment/normalized_alignmentwithcharacters,character_start_times_seconds, andcharacter_end_times_secondsfor every character:
{
"audio_base64": "<base64 mp3 bytes>",
"alignment": {
"characters": ["H", "i"],
"character_start_times_seconds": [0.0, 0.12],
"character_end_times_seconds": [0.12, 0.25]
},
"normalized_alignment": { "characters": ["H", "i"], "character_start_times_seconds": [0.0, 0.12], "character_end_times_seconds": [0.12, 0.25] }
}/stream/with-timestampsreturns a run of the JSON objects above (not SSE); parse them one object at a time.- Billing counts the Unicode characters of
text, regardless of output format or audio length; unit prices are in the console's model list. - Authenticate with
Authorization: Bearer <API key>;x-api-keyand thexi-api-keyheader that the official ElevenLabs SDKs send also work, so an official SDK only needs its base URL changed tohttps://api.modelgo.aiand a ModelGo API key. When several auth headers are present,Authorizationwins. Errors use the platform's unified error format; see Errors & Rate Limits.
WebSocket input streaming
For text that arrives in pieces, such as LLM output being spoken as it is generated: the client sends text in chunks and the server returns audio in chunks.
| Item | Value |
|---|---|
| Full URL | wss://api.modelgo.ai/v1/text-to-speech/{voice_id}/stream-input?model_id=<logical model name> |
| Query | model_id (required); optional output_format, language_code, sync_alignment, apply_text_normalization, seed. Other official parameters are ignored |
| Auth | An xi-api-key or Authorization: Bearer <API key> header on the handshake, or — the official browser style — xi_api_key in the first message |
Client messages (JSON text frames):
{"text": " ", "voice_settings": {"stability": 0.5, "similarity_boost": 0.8}}
{"text": "Hello, this is an input streaming test. ", "flush": true}
{"text": ""}- The first message initializes the session. Its
textis usually a single space and it may carryvoice_settings(onlystabilityandsimilarity_boosttake effect). - Each later message appends text;
flush: trueasks for the text received so far to be synthesized now. {"text": ""}ends the input. The server sends the remaining audio, then{"isFinal": true}, and closes with1000.generation_config,try_trigger_generation, pronunciation dictionaries, and similar fields are ignored.
Server messages:
{"audio": "<base64 audio chunk>", "isFinal": null, "normalizedAlignment": {…}, "alignment": {…}}
{"isFinal": true}audio may be an empty string; skip it.
Handshake errors (before the upgrade, as an HTTP status with a body like {"error":{"type":"invalid_request_error","code":"…","message":"…"}}):
| HTTP | Meaning |
|---|---|
| 400 | Missing model_id or voice_id, or not a WebSocket upgrade request |
| 401 | Missing or invalid API key |
| 402 | Insufficient balance or budget |
| 403 | Not allowed to call this model, or the model does not offer the WebSocket endpoint |
| 404 | Unknown model or no available route |
| 503 | No provider available right now; retry later |
With first-message auth the connection is already upgraded, so these refusals arrive as an error message followed by a close (1011 for the 5xx cases, 1008 otherwise).
Errors during a session: the server first sends {"message": "…", "error": "<code>", "code": <close code>}, then closes with that code:
| Case | error | Close code |
|---|---|---|
| More than 5000 characters of text on one connection | max_characters_exceeded | 1008 |
| The connection reaches its 10-minute limit | max_session_duration | 1008 |
| A message is not a JSON text frame | invalid_message | 1003 / 1007 |
| No first message in time (20 s with header auth, 10 s with first-message auth) | initial_message_timeout | 1008 |
| The provider connection fails | provider_unavailable | 1011 |
Billing counts the Unicode characters of the text actually sent for synthesis on the connection (the initializing space, the end marker, and whitespace-only keep-alive messages without flush are free). 5000 characters are reserved when the socket opens and the actual count is settled when it closes; nothing is charged if no character was sent. The platform keeps the provider connection alive while the client is idle, so the client does not need to send keep-alive messages.