First pot at the mountain tea house — A warm Mandarin welcome with a soft laugh in the middle.
ElevenLabs V4 Text to Speech API
elevenlabs/tts/eleven-v4ElevenLabs V4 Text to Speech turns written scripts into emotionally expressive narration with inline audio tags and support for 90+ languages. Tune stability and voice similarity for each scene, then choose MP3, PCM, Opus or telephony audio.
Input
Examples
ElevenLabs V4 Text to Speech API
Generate speech with the API
Submit a script, keep the returned task_id, and retrieve the completed audio through the task status endpoint.
Authenticate your request
Send your Vidgo API key in the Authorization header using Bearer authentication.
- Endpoint
- POST
https://api.vidgo.ai/api/generate/submit - Authentication
- Authorization: Bearer YOUR_API_KEY
Submit the script
Send model and input as JSON. The example below uses the same script and voice as the first audio sample.
REQUEST_BODY=$(cat <<'JSON'
{
"model": "elevenlabs/tts/eleven-v4",
"input": {
"text": "[calm] Night three at the lighthouse. The fog rolled in early, thick as wool. [whispers] At midnight, something knocked on the door... three slow knocks. [nervous laugh] I told myself it was only the wind. [quietly] Then I found wet footprints on the stairs, leading up, and never back down.",
"voice": "George",
"stability": 0.45,
"similarity_boost": 0.75,
"output_format": "mp3_44100_128",
"apply_text_normalization": "auto",
"timestamps": false,
"language_code": "en"
}
}
JSON
)
RESPONSE=$(curl --silent --show-error --fail-with-body \
--request POST \
--url "https://api.vidgo.ai/api/generate/submit" \
--header "Authorization: Bearer $VIDGO_API_KEY" \
--header "Content-Type: application/json" \
--data "$REQUEST_BODY")
CODE=$(printf '%s' "$RESPONSE" | jq -r '.code // empty')
if [ "$CODE" != "0" ] && [ "$CODE" != "200" ]; then
printf 'API error: %s
' "$RESPONSE" >&2
exit 1
fi
printf '%s
' "$RESPONSE"Follow the task
Query the returned task_id until status is finished or failed.
Status endpoint
GET https://api.vidgo.ai/api/generate/status/{task_id}Keep the task_id from submission. Check its status and read the generated files when it finishes.
not_startedrunningfinishedfailed{
"code": 200,
"data": {
"task_id": "BAMMBL0SN12PBO8U",
"status": "not_started",
"created_time": "2026-11-05T07:13:33"
}
}{
"code": 200,
"data": {
"task_id": "BAMMBL0SN12PBO8U",
"status": "finished",
"files": [
{
"file_url": "https://cdn.vidgo.ai/apis/models/elevenlabs/tts/eleven-v4/v2/01/speech-01.mp3",
"file_type": "audio",
"file_name": "speech-01.mp3"
}
],
"created_time": "2026-11-05T07:13:33",
"error_message": null,
"progress": 100
}
}Read the audio files
Use the audio file_url for playback or download. With timestamps enabled, download the timing file from its separate files entry.
set -euo pipefail
: "${VIDGO_API_KEY:?Set VIDGO_API_KEY in your environment}"
REQUEST_BODY=$(cat <<'JSON'
{
"model": "elevenlabs/tts/eleven-v4",
"input": {
"text": "[calm] Night three at the lighthouse. The fog rolled in early, thick as wool. [whispers] At midnight, something knocked on the door... three slow knocks. [nervous laugh] I told myself it was only the wind. [quietly] Then I found wet footprints on the stairs, leading up, and never back down.",
"voice": "George",
"stability": 0.45,
"similarity_boost": 0.75,
"output_format": "mp3_44100_128",
"apply_text_normalization": "auto",
"timestamps": false,
"language_code": "en"
}
}
JSON
)
SUBMIT_RESPONSE=$(curl --silent --show-error --fail-with-body \
--request POST \
--url "https://api.vidgo.ai/api/generate/submit" \
--header "Authorization: Bearer $VIDGO_API_KEY" \
--header "Content-Type: application/json" \
--data "$REQUEST_BODY")
TASK_ID=$(printf '%s' "$SUBMIT_RESPONSE" | jq -r '.data.task_id // .task_id // empty')
BUSINESS_CODE=$(printf '%s' "$SUBMIT_RESPONSE" | jq -r '.code // empty')
if [ "$BUSINESS_CODE" != "0" ] && [ "$BUSINESS_CODE" != "200" ]; then
printf 'Submit failed:
%s
' "$SUBMIT_RESPONSE" >&2
exit 1
fi
if [ -z "$TASK_ID" ]; then
printf 'Submit response did not include task_id:
%s
' "$SUBMIT_RESPONSE" >&2
exit 1
fi
START_TIME=$(date +%s)
POLL_DELAY=2
while true; do
if [ $(( $(date +%s) - START_TIME )) -ge 600 ]; then
printf 'Timed out after 600 seconds
' >&2
exit 1
fi
STATUS_RESPONSE=$(curl --silent --show-error --fail-with-body \
--url "https://api.vidgo.ai/api/generate/status/$TASK_ID" \
--header "Authorization: Bearer $VIDGO_API_KEY")
STATUS=$(printf '%s' "$STATUS_RESPONSE" | jq -r '.data.status // .status // empty')
BUSINESS_CODE=$(printf '%s' "$STATUS_RESPONSE" | jq -r '.code // empty')
if [ "$BUSINESS_CODE" != "0" ] && [ "$BUSINESS_CODE" != "200" ]; then
printf 'Status request failed:
%s
' "$STATUS_RESPONSE" >&2
exit 1
fi
case "$STATUS" in
finished)
printf '%s' "$STATUS_RESPONSE" | jq -r '(.data.files // .files // [])[]?.file_url'
break
;;
failed)
printf '%s' "$STATUS_RESPONSE" | jq -r '.data.error_message // .error_message // "Generation failed"' >&2
exit 1
;;
not_started|running)
sleep "$POLL_DELAY"
if [ "$POLL_DELAY" -lt 10 ]; then POLL_DELAY=$((POLL_DELAY + 1)); fi
;;
*)
printf 'Unexpected task status: %s
' "$STATUS" >&2
exit 1
;;
esac
doneRequest parameters
Place speech controls inside input; keep model and callback_url at the top level.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| model | string | Yes | — | Use elevenlabs/tts/eleven-v4 to select this speech model. |
| input | object | Yes | — | Contains the script and the speech controls listed below. |
| input.text | string | Yes | — | The script to speak, 1–5,000 characters as sent, including spaces, line breaks and audio tags. Add tags such as [whispers] or [excited] where the delivery should change. |
| input.voice | string | No | Rachel | An ElevenLabs voice name or voice ID. Pick a suggested preset such as Rachel, George or Lily, or paste the ID of another ElevenLabs voice. |
| input.stability | number | No | 0.5 | A value from 0 to 1. Lower values allow more expressive variation; higher values keep the read steadier from line to line. |
| input.similarity_boost | number | No | 0.75 | A value from 0 to 1 that sets how closely the speech follows the character of the selected voice. |
| input.output_format | string | No | mp3_44100_128 | Codec, sample rate and bitrate of the returned audio: MP3 at 22.05 or 44.1 kHz, raw PCM from 8 to 48 kHz, µ-law and A-law at 8 kHz for telephony, or Opus at 48 kHz. Values: mp3_22050_32, mp3_44100_32, mp3_44100_64, mp3_44100_96, mp3_44100_128, mp3_44100_192, pcm_8000, pcm_16000, pcm_22050, pcm_24000, pcm_44100, pcm_48000, ulaw_8000, alaw_8000, opus_48000_32, opus_48000_64, opus_48000_96, opus_48000_128, opus_48000_192. |
| input.apply_text_normalization | string | No | auto | auto decides when to spell out numbers, dates and symbols, on always normalizes them, and off reads text that you have already written out for speech. |
| input.timestamps | boolean | No | false | Set to true to receive timing data for the speech as an additional file next to the audio. |
| input.language_code | string | No | — | Optional ISO 639-1 code, such as en, zh or es, to name the spoken language. Leave it empty to let the model detect the language from the script. |
| callback_url | string | No | — | Optional HTTP or HTTPS URL for task notifications. Include it at the top level of the request. |
Response fields
Submission identifies the task. Status responses report progress toward the completed audio.
| Field | Type | Description |
|---|---|---|
| code | integer | Response code. A successful response uses 200 or 0. |
| data.task_id | string | Task identifier used to query status. |
| data.status | string | Task state: not_started, running, finished, or failed. |
| data.created_time | string | Task creation time returned by the service. |
| data.files | array | Generated files. Read audio entries for speech and other entries for requested attachments. |
| data.files[].file_url | string | URL used to play or download a generated file. |
| data.files[].file_type | string | audio for speech; other for attachments such as the timing file. |
| data.files[].file_name | string | File name, when returned. |
| data.error_message | string | null | Error details when a task fails. |
Task lifecycle
Use the task state to decide when to check again or read the result.
not_startedThe task has been accepted and is waiting to start.
runningSpeech generation is in progress. Continue checking the same task.
finishedGeneration is complete. Read files for the audio and requested attachments.
failedGeneration failed. Read error_message before deciding whether to submit a new request.
Handle results and interruptions
- Keep the current task identifierIf a status request is interrupted, query the same task_id again to recover its latest state.
- Match each file to its purposeUse file_type to separate speech from attachments. Read the returned file_url and file_name for each download.
- Inspect a failed taskWhen status is failed, read error_message and review the script and settings before submitting another generation.
Speech specifications
| Item | Value | Description |
|---|---|---|
| Task | Text to Speech | Generate speech from a script and one voice. |
| Text length | 1–5,000 characters | Counted as sent, including spaces and tags. |
| Languages | 90+ | Detected from the script or set with language_code. |
| Output formats | 19 | MP3, PCM, µ-law, A-law and Opus; default mp3_44100_128. |
| Price | $0.08 / 1K characters | 16 credits per 1,000 characters. |
| Timing data | Optional | Returned as an extra file when timestamps is true. |
Explore more speech models
ElevenLabs V4 Text to Speech API frequently asked questions
What is the ElevenLabs V4 Text to Speech API?
ElevenLabs V4 Text to Speech is an ElevenLabs model for turning scripts into expressive speech. It generates narration and character lines in 90+ languages, with inline audio tags for emotion, pauses and reactions. It interprets tags together with punctuation and context while you keep control of the voice, stability, similarity and output format. You can call it programmatically or try it from the playground above.
How is ElevenLabs V4 different from ElevenLabs V3?
ElevenLabs V4 is the newer generation, released in September 2026, with support for 90+ languages compared with 70+ for V3. On Vidgo it also adds Similarity Boost, a choice of 19 output formats and voice IDs beyond the preset list, while the price stays at 16 credits ($0.08) per 1,000 characters.
How do audio tags work in ElevenLabs V4?
Write a direction in square brackets inside Text, such as [whispers], [laughs] or [excited], right before the words it should shape. Tags count toward the character total. Start with one tag per phrase and listen before adding more.
How long can a ElevenLabs V4 script be?
Each request accepts 1–5,000 characters, counted exactly as sent, including spaces, line breaks and tags. Split longer scripts at paragraph or scene breaks and reuse the same voice settings for every part.
Which output formats does ElevenLabs V4 return?
Output Format offers 19 values: MP3 at 22.05 or 44.1 kHz from 32 to 192 kbps, raw PCM from 8 to 48 kHz, µ-law and A-law at 8 kHz, and Opus at 48 kHz from 32 to 192 kbps. The default is mp3_44100_128.
Can I use my own ElevenLabs voice with ElevenLabs V4?
Voice accepts an ElevenLabs voice name or voice ID. Use a suggested preset such as Rachel or George, or paste the ID of another ElevenLabs voice and keep Stability and Similarity Boost fixed while you compare takes.
Can ElevenLabs V4 return timestamps for subtitles?
Yes. Set timestamps to true and the finished task includes a timing file as an additional item in files, next to the audio. Use it to place captions or align edits.
How much does ElevenLabs V4 cost per request?
ElevenLabs V4 costs 16 credits ($0.08) per 1,000 characters. A 1,000-character script costs 16 credits and a full 5,000-character request costs 80 credits ($0.40).


