Base URL https://api.echonova.ai

Developers

Text to Speech voices and options

The voice list and what each voice field means, how to pick a voice by language, and every request option with its type, range, default and effect.

This page is the lookup table for Text to Speech: what the voices endpoint returns, how to choose a voice, and what each field of a generation request does. It applies to both POST /v1/text-to-speech/sync and POST /v1/text-to-speech, which accept the same body.

Voices endpoint

GET https://api.echonova.ai/v1/text-to-speech/voices

The call takes no parameters. It returns every voice whose status is active, newest first, in one response with no paging. data.total is the count.

Field Type Always present Meaning
id string Yes The value to send as voiceId.
name string Yes The voice's identifier, for example ae_ahmed. Unique per voice.
displayName string Yes The human label, for example Ahmed. Falls back to name when no label is set.
language string Yes ar for Arabic voices, en for English voices.
gender string Optional in the schema; every voice carries one The gender label, for example Male.
previewUrl string No Declared in the API schema and absent from the response. Preview a voice in the portal under Voices.
{
  "data": {
    "items": [
      {
        "id": "66e5ef3a9c1d2b4f8a7e6c01",
        "name": "ae_ahmed",
        "displayName": "Ahmed",
        "language": "ar",
        "gender": "Male"
      }
    ],
    "total": 1
  },
  "success": true,
  "statusCode": 200,
  "errorMessage": null
}
curl https://api.echonova.ai/v1/text-to-speech/voices \
  -H "Authorization: Bearer YOUR_API_KEY"

Choosing a voice

Filter on language in your own code; the endpoint has no filters. ar selects the Arabic voices and en the English ones.

The voice object does not include its dialect. The dialect is fixed by the voice: you send voiceId and nothing else, and the audio comes out in that voice's dialect. To see which dialect a voice speaks, open Voices in the portal. Each card shows the voice's display name and its dialect. Match the card to the API by displayName, or by name when no display name is set, and use that voice's id in your request.

The dialects are egyptian, emirati, saudi, kuwaiti, qatari, omani, lebanese, syrian, iraqi, fusha, american, british and the legacy english. Which of them have voices at a given time can change; the voices call is the source of truth. English voices report language en whatever their dialect.

The set of voices is not fixed. A voice can be withdrawn: it drops out of the list, and a request that names it gets 404 Voice not found. Cache the list for convenience, but refresh it when that error appears.

Choose a voice once and keep its id in configuration, with a path back to the list when the id stops working.

Recommended

  • Fetch the list at start-up, pick by displayName and language, keep the id in your configuration, and refetch on 404 Voice not found.

Less effective

  • Call GET /v1/text-to-speech/voices before every generation and take the first Arabic voice, whichever it is that day.

Request options

Both generation endpoints take a JSON body with these fields. Leading and trailing whitespace is trimmed from every string before validation. Unknown fields are rejected.

Option Type Required Range Default Effect
text string Yes 1 to 500 characters after trimming none The text to speak. Every character is billed, spaces and line breaks included.
voiceId string Yes An id from the voices call none Selects the voice and, through it, the language and dialect.
speed number No 0.5 to 2 1 Sets the reading pace. Lower is slower, higher is faster.
cfg number No 1 to 5 2 Classifier-free guidance scale. Leave it at the default unless you are tuning against your own listening tests.
numSteps number No Exactly one of 16, 32, 48, 64, 128 48 Diffusion step count. Only these five values are accepted; it is not a range.
tashkeel boolean No true or false false Adds diacritics to the text before synthesis. See the note below.

An omitted speed, cfg or numSteps means "use the default", and the job records the value that was used.

About tashkeel

  • tashkeel applies to Fusha voices only. On a voice of any other dialect the text is synthesised as written, without diacritics, and the call still succeeds.
  • tashkeel takes effect on POST /v1/text-to-speech/sync only. POST /v1/text-to-speech accepts the field and synthesises the text as written. To get diacritised speech, use the synchronous endpoint.
  • text on the job is the text you sent; the diacritised text is not returned.

Example with every option

curl -X POST https://api.echonova.ai/v1/text-to-speech/sync \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "الطقس اليوم جميل والسماء صافية في المدينة",
    "voiceId": "VOICE_ID",
    "speed": 1,
    "cfg": 2,
    "numSteps": 48,
    "tashkeel": true
  }'

Output

Both endpoints produce one audio file per call and expose it as audioUrl: in the synchronous response directly, and on the job object once the asynchronous job is completed.

Fact Value
What audioUrl is A signed link to the audio file. It expires. GET /v1/text-to-speech/jobs/{jobId} returns a fresh link on every call.
File format WAV. The file name in the link ends with .wav. No request option chooses the format.
durationSeconds The length of the audio in seconds. The synchronous response carries 0 when the length could not be measured; the job object of an asynchronous job omits the field until the job is completed.
creditsConsumed The credits charged for this call.

Credits

A call is priced from the text field before the job starts: every character of text, spaces and line breaks included, at 1 credit per character. The workspace balance is charged before generation and refunded if the job fails. Rates, worked examples and the credit limit per key are on Credits.

Limits

Limit Value Applies to
text 1 to 500 characters after trimming POST /v1/text-to-speech/sync and POST /v1/text-to-speech
speed 0.5 to 2 Both endpoints
cfg 1 to 5 Both endpoints
numSteps 16, 32, 48, 64 or 128 Both endpoints
Voices per response All active voices, no paging GET /v1/text-to-speech/voices
Requests per key 100 per 60 seconds on each endpoint; see Rate limits All calls

The two endpoints share one text limit. The difference between them is how you receive the result, not how much text they take.

Errors

Every error uses the standard envelope with success: false and the message in errorMessage. Validation failures list every failing rule in one message, separated by commas.

Status errorMessage When What to do
400 text must be longer than or equal to 1 characters, text should not be empty text is an empty string or only whitespace. When text is missing, text must be shorter than or equal to 500 characters is prepended and text must be a string appended. Send at least one non-whitespace character.
400 text must be shorter than or equal to 500 characters text is over the limit. Split the text into several calls.
400 voiceId should not be empty voiceId is an empty string. When it is missing, voiceId must be a string is appended. Send an id from the voices call.
400 speed must not be less than 0.5, speed must not be greater than 2 speed is out of range. The same pattern applies to cfg with 1 and 5. Stay inside the range.
400 speed must be a number conforming to the specified constraints speed or cfg is not a number. Send a JSON number, not a string.
400 numSteps must be one of the following values: 16, 32, 48, 64, 128 numSteps is not one of the five values. Send one of the five values.
400 tashkeel must be a boolean value tashkeel is not true or false. Send a JSON boolean.
400 property <name> should not exist The body carries a field the endpoint does not know. Remove it.
400 Invalid identifier voiceId is not a well-formed id. Copy the id exactly as the voices call returned it.
400 A free-text reason, at most 300 characters The request was rejected with a reason of its own. Rare. Read the message and fix the request.
402 Insufficient subscription balance The workspace balance cannot cover the charge. Add credits to the workspace. See Credits.
403 This API key does not have access to Text-to-Speech services The key restricts services and Text to Speech is not among them. Grant Access to Text to Speech on the key.
404 Voice not found No active voice has this voiceId. List the voices again and pick an active one.
429 Monthly spending limit exhausted The key's credit limit for this cycle would be exceeded. Raise or clear the limit on the key.
503 Speech generation is temporarily unavailable. Please try again. Speech generation was unavailable for the request. The charge is refunded. Retry after a short wait.

Authentication failures, 401 and the other 403, are listed on Authentication. The request rate 429 is on Rate limits.

FAQ

How do I know which dialect a voice speaks?

Open Voices in the portal; each card shows the dialect. The API voice object carries language only.

Why does tashkeel do nothing on my job?

Either the voice is not Fusha, or you used POST /v1/text-to-speech. Diacritics are added on POST /v1/text-to-speech/sync with a Fusha voice.

Can I preview a voice through the API?

No. previewUrl is absent from the response. Preview voices in the portal under Voices, or generate a short sentence with POST /v1/text-to-speech/sync.

What do cfg and numSteps change?

Both tune the synthesis. Leave them at their defaults unless you are comparing outputs in your own listening tests; the accepted values are in the table above, and anything outside them is rejected with 400.

Can I get MP3 or OGG?

No. The audio is WAV. Convert it on your side if you need another format.