ElevenLabs Audio

MethodPathUseMarketplace protocol
POST/v1/text-to-speech/{voice_id}Synthesize speech and return the full audioElevenLabs Text to Speech
POST/v1/text-to-speech/{voice_id}/streamSynthesize speech and stream audio as it is generatedElevenLabs Text to Speech
POST/v1/text-to-speech/{voice_id}/with-timestampsSynthesize speech with character-level timestampsElevenLabs Text to Speech with Timestamps
POST/v1/text-to-speech/{voice_id}/stream/with-timestampsStream audio together with timestampsElevenLabs Text to Speech with Timestamps
GET/v1/text-to-speech/{voice_id}/stream-inputWebSocket: send text incrementally, receive audio incrementallyElevenLabs 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

bash
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
ParameterWhereNotes
voice_idPathElevenLabs voice ID
output_formatQueryOutput format such as mp3_44100_128 or pcm_16000; same values as the official API
model_idBody, requiredPlatform logical model name. ElevenLabs defaults it to eleven_multilingual_v2; here it must be set explicitly
textBody, requiredText to synthesize
Other official fieldsBodyvoice_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); /stream sends them as they are generated.
  • /with-timestamps returns JSON: audio in audio_base64 (Base64), and alignment / normalized_alignment with characters, character_start_times_seconds, and character_end_times_seconds for every character:
json
{
  "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-timestamps returns 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-key and the xi-api-key header that the official ElevenLabs SDKs send also work, so an official SDK only needs its base URL changed to https://api.modelgo.ai and a ModelGo API key. When several auth headers are present, Authorization wins. 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.

ItemValue
Full URLwss://api.modelgo.ai/v1/text-to-speech/{voice_id}/stream-input?model_id=<logical model name>
Querymodel_id (required); optional output_format, language_code, sync_alignment, apply_text_normalization, seed. Other official parameters are ignored
AuthAn 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):

json
{"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 text is usually a single space and it may carry voice_settings (only stability and similarity_boost take effect).
  • Each later message appends text; flush: true asks 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 with 1000.
  • generation_config, try_trigger_generation, pronunciation dictionaries, and similar fields are ignored.

Server messages:

json
{"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":"…"}}):

HTTPMeaning
400Missing model_id or voice_id, or not a WebSocket upgrade request
401Missing or invalid API key
402Insufficient balance or budget
403Not allowed to call this model, or the model does not offer the WebSocket endpoint
404Unknown model or no available route
503No 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:

CaseerrorClose code
More than 5000 characters of text on one connectionmax_characters_exceeded1008
The connection reaches its 10-minute limitmax_session_duration1008
A message is not a JSON text frameinvalid_message1003 / 1007
No first message in time (20 s with header auth, 10 s with first-message auth)initial_message_timeout1008
The provider connection failsprovider_unavailable1011

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.