Text to Speech
Turn a script into speech with any model and any voice, in Algerian, Tunisian or Libyan darija
curl -X POST https://api.voice213.com/api/v1/tts \
-H "Authorization: Bearer $VOICE213_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "(نبرة حماسية ومنعشة) خويا، شفت الساعة الجديدة؟ مقاومة للماء وبسوار جلدي أصلي!",
"model": "voice213-pro-2",
"voice": "ryad",
"dialect": "algerian"
}'import os
import requests
response = requests.post(
"https://api.voice213.com/api/v1/tts",
headers={"Authorization": f"Bearer {os.environ['VOICE213_API_KEY']}"},
json={
"text": "(نبرة حماسية ومنعشة) خويا، شفت الساعة الجديدة؟ مقاومة للماء وبسوار جلدي أصلي!",
"model": "voice213-pro-2",
"voice": "ryad",
"dialect": "algerian",
},
timeout=300,
)
print(response.json())const response = await fetch("https://api.voice213.com/api/v1/tts", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VOICE213_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
text: "(نبرة حماسية ومنعشة) خويا، شفت الساعة الجديدة؟ مقاومة للماء وبسوار جلدي أصلي!",
model: "voice213-pro-2",
voice: "ryad",
dialect: "algerian",
}),
});
console.log(await response.json());curl -X POST https://api.voice213.com/api/v1/tts \
-H "Authorization: Bearer $VOICE213_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "مرحبا بيكم في Voice213",
"voiceId": "nariman",
"dialect": 1,
"model": "voice213-lite"
}'{
"success": true,
"audioUrl": "https://pub-5d56f47943d84bbda9b38f1a6f9962e6.r2.dev/tts/66f1c0a4e1b2c3d4e5f60718/1727254800000-9f3c2a7b1d4e6f80.mp3",
"model": "voice213-pro-2",
"voice": "ryad",
"cost": 1.2,
"currency": "DZD",
"chars": 56,
"billableChars": 100,
"remainingCredit": 4998.8,
"words": 9,
"quality": "pro2",
"dailyProCount": 0,
"dailyProLimit": null
}{
"success": true,
"data": {
"audio": "data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAwF0AAIC7AAACABAAZGF0YQAAAAA...",
"format": "audio/wav",
"model": "voice213-lite",
"textLength": 22,
"wordCount": 4,
"chars": 22,
"billableChars": 100,
"cost": 0.9,
"currency": "DZD",
"remainingCredit": 4997.9
}
}{
"error": "UNKNOWN_MODEL",
"message": "Unknown model 'voice213-ultra'. Use one of: voice213-lite, voice213-lite-2, voice213-pro, voice213-pro-1.5, voice213-pro-2 (see GET /v1/models).",
"models": ["voice213-lite", "voice213-lite-2", "voice213-pro", "voice213-pro-1.5", "voice213-pro-2"]
}{
"error": "UNKNOWN_VOICE",
"message": "Unknown voice 'sarah'. Use a voice id from GET /v1/tts/voices.",
"voices": ["sohila", "nariman", "ryad", "amine", "ilyas", "sofia", "yanis", "ines", "chaima", "radwan", "mehdi", "zouhir", "mahmoud", "samir", "meriem", "yasmine", "bilal", "karim", "amel", "nour", "sirine", "mourad", "walid", "idir", "hamza", "rachid", "malika", "kenza", "thiziri", "hanane"]
}{
"error": "EMPTY_TEXT",
"message": "Nothing to speak: the text is empty or contains only tone directions."
}{
"error": "TEXT_TOO_LONG",
"message": "Too much of the text isn't spoken: tone directions, punctuation and invisible characters can't be more than about the spoken text itself. Remove them or split the text.",
"chars": 1842,
"maxChars": 1300
}{
"error": "INSUFFICIENT_CREDIT",
"message": "Not enough API credit. Top up your balance in the developer workspace.",
"required": 1.2,
"balance": 0.35,
"currency": "DZD"
}{
"error": "Rate limit exceeded: RPM limit exceeded",
"limit": 600
}{
"error": "Rate limit exceeded: Concurrent request limit exceeded",
"limit": 20
}{
"error": "هذا النموذج الصوتي غير متاح مؤقتًا. اختر نموذجًا آخر أو حاول لاحقًا.",
"code": "MODEL_DISABLED",
"model": "voice213-pro-2",
"messages": {
"ar": "هذا النموذج الصوتي غير متاح مؤقتًا. اختر نموذجًا آخر أو حاول لاحقًا.",
"en": "This voice model is temporarily unavailable. Pick another model or try again later.",
"fr": "Ce modèle de voix est temporairement indisponible. Choisissez-en un autre ou réessayez plus tard."
}
}Turns a script into speech in Algerian, Tunisian or Libyan darija. It's the one endpoint for every voice-over: choose the model with model and the voice with voice, and switch either one from one request to the next.
{
"text": "مرحبا بيكم في Voice213",
"model": "voice213-pro-2",
"voice": "kenza"
}The endpoint answers in one of two formats. Both take all five models and all 30 voices:
- Studio format, the default: you get a link to an MP3 file, and the voice-over is saved to your History in the web app.
- Inline format: name the voice with
voiceIdinstead ofvoice, and the audio comes back inside the response as a WAV data URL. Nothing is stored.
Voice213 picks the format from the body: a request with a voiceId, or with a dialect of 1, 2 or 3, uses the inline format; any other request uses the studio format.
Pricing
Voice-overs are billed per character of text, spaces and punctuation included, at the price of the model you choose:
| Model | model | Per 1,000 characters | Per million characters |
|---|---|---|---|
| Voice213 LiteDefault | voice213-lite | 9 DA | 9,000 DA |
| Voice213 Lite 2New | voice213-lite-2 | 8 DA until 31 December 2026, then 15 DA | 8,000 DA until 31 December 2026, then 15,000 DA |
| Voice213 Pro | voice213-pro | 20 DA | 20,000 DA |
| Voice213 Pro 1.5 | voice213-pro-1.5 | 39 DA | 39,000 DA |
| Voice213 Pro 2New | voice213-pro-2 | 12 DA until 31 December 2026, then 24 DA | 12,000 DA until 31 December 2026, then 24,000 DA |
- Every request is billed at least 100 characters, so a short line costs the same as a 100-character one.
- Tone directions such as
(نبرة واثقة وهادئة)are performed, not read, and aren't billed when they're at most 100 characters long, parentheses included. A longer one is billed like the rest of the text. - A text with nothing to speak, such as one made only of tone directions, is refused with
400 EMPTY_TEXT. - Tone directions stay free, but they can't outweigh the spoken text: a text may be at most twice its billable characters plus 1,000 characters long, or it's refused with
400 TEXT_TOO_LONG. See Too much unspoken text. - Nothing is charged when a voice-over fails. While one runs, its price is set aside from your API credit: see Several voice-overs at once.
To know the price before you generate, send the same body to Estimate Cost. It's free. See API Pricing & Limits for worked examples.
Authorizations
Your API key as a Bearer token: Bearer sk_live_.... You can send it in an x-api-key header instead. See Authentication.
Body
application/jsonThe script to speak, up to 5,000 words in either format. Every word counts toward the limit, tone directions included. A text with nothing to speak, such as one made only of tone directions, is refused with 400 EMPTY_TEXT. A text longer than twice its billable characters plus 1,000, every character counted, is refused with 400 TEXT_TOO_LONG. Long texts take longer to generate: split very long scripts.
voice213-liteThe voice model: voice213-lite, voice213-lite-2, voice213-pro, voice213-pro-1.5 or voice213-pro-2. Capitalization doesn't matter, and the short forms lite, lite-2, pro, pro-1.5 and pro-2 work too. An unknown model is refused with 400 UNKNOWN_MODEL, which lists the models you can use, and a model paused for the API with 503 MODEL_DISABLED. See List Models for their prices, and Voice Models to choose one.
Inline format: the voice, any of the 30 voice IDs that List Voices returns. Capitalization doesn't matter. Sending voiceId switches the request to the inline format. An unknown ID is refused with 400 Invalid voice ID.
algerianThe dialect: algerian, tunisian or libyan. The numbers 1, 2 and 3 work too, but they switch the request to the inline format, which reads the voice from voiceId and ignores voice (without voiceId, Nariman speaks). With voice, send the dialect by name. Any other value is read as Algerian. See Dialects.
Studio format: an approximate length for the voice-over, in seconds. The voice adjusts its pace to come close to it, for example to fit a 15-second video.
The older name for the model, still accepted so existing integrations keep working: standard (Voice213 Lite), lite2, pro, pro1.5 or pro2. Unlike model, an unknown quality isn't refused: it uses Voice213 Lite. When a request has both, model wins. Prefer model in new code.
Response: studio format
application/jsontrue.
A link to the voice-over, an MP3 file at 128 kbps. The voice-over is also saved to your History in the web app. In the rare case the file can't be stored, audioUrl carries the MP3 itself as a data URL (data:audio/mp3;base64,…) and nothing is saved to History, so check for the data: prefix before downloading it.
The model that spoke, such as voice213-pro-2.
The voice that spoke, such as kenza.
What the request cost, in dinars.
DZD.
The characters of text, without its free tone directions (those of up to 100 characters) and with runs of spaces counted once.
The characters billed: chars, or 100 when chars is lower.
Your API credit after the request, in dinars.
The words in text, not counting tone directions, punctuation and emoji. The 5,000-word limit counts tone directions too.
The model under its older name, such as pro2, for integrations that still read it.
Always 0: API requests don't use the web app's daily Pro allowance.
Always null: the API has no daily limit on Pro models.
How the voice performs your script
- The default delivery is a warm, confident advertising voice. Tone directions in parentheses change it for the text that follows.
- Common spelling mistakes are corrected before the text is spoken, and prices and percentages are read the local way:
2500 دجbecomes «خمسة وعشرين مية دينار». - French and English words are pronounced the way a local speaker says them.
- Tashkeel in the text is respected: a diacritized word is pronounced as written.
Several voice-overs at once
One key can have up to 20 voice-overs running at the same time. A voice-over runs until its audio is ready or it fails, even if your client stopped waiting for it. Once 20 are running, the next one gets 429 Rate limit exceeded: Concurrent request limit exceeded: retry when one of them has ended. See Rate Limits.
Each running voice-over also sets its price aside from your API credit until it's charged (when it succeeds) or fails (nothing is charged). A voice-over that arrives while others run is refused with 402 INSUFFICIENT_CREDIT when what's left after them can't cover it. The balance in that 402 is your API credit less what the others have set aside. See Insufficient API credit.
Errors
| Status | Error | When |
|---|---|---|
400 | text is required | Studio format without text, or with only spaces. |
400 | Missing 'text' in request body | Inline format without text, or with only spaces. |
400 | UNKNOWN_MODEL | model isn't one of the five models. The body lists the models you can use. |
400 | UNKNOWN_VOICE | Studio format with a voice that isn't one of the 30 voice IDs. The body lists the voices. |
400 | Invalid voice ID: '<id>' | Inline format with a voiceId that isn't one of the 30 voice IDs. |
400 | EMPTY_TEXT | text has nothing to speak, such as a text made only of tone directions. |
400 | Max 5000 words exceeded | text is over 5,000 words, tone directions included, in either format. The body has words and maxWords. |
400 | TEXT_TOO_LONG | text is longer than twice its billable characters plus 1,000: too much of it is tone directions, spaces or invisible characters. The body has chars and maxChars. |
401 | Missing API key. Pass it as 'Authorization: Bearer sk_live_...' | The request has no key. |
401 | Invalid or inactive API key | The key is wrong, switched off or deleted. |
402 | INSUFFICIENT_CREDIT | Your API credit, less what your other running voice-overs have set aside, can't cover the request. The body has required and balance. |
403 | Account deactivated | The account that owns the key is deactivated or suspended. |
403 | API_ACCESS_BLOCKED (in code) | API access is switched off for the account. |
429 | Rate limit exceeded: RPM limit exceeded or Rate limit exceeded: Daily limit exceeded | Your key's requests per minute or per day. |
429 | Rate limit exceeded: Concurrent request limit exceeded | Your key already has 20 voice-overs running. Retry when one of them ends. |
500 | Internal server error processing TTS request | Inline format: the voice-over couldn't be generated. Retry. Nothing was charged. |
500 | Server error | Studio format: something went wrong on our side, such as reading or charging your API credit. Retry. Nothing was charged. |
502 | Failed to generate audio. No points consumed. | Studio format: the voice-over couldn't be generated. Retry. Nothing was charged. |
503 | MODEL_DISABLED (in code) | The model is paused for the API. The body names it in model. Use another model. |
503 | TOOL_DISABLED (in code) | Voice-overs through the API are paused. Retry later. |
503 | MAINTENANCE (in code) | The API is down for maintenance. Retry later. |
See Errors for the full list.
Was this page helpful?