> For the full page index, fetch https://echonova.ai/llms.txt

# Generate speech from text

Starts a Text to Speech job for 1 to 500 characters and returns its jobId. Get the audio from GET /v1/text-to-speech/jobs/{jobId} or the job.completed webhook. Cloned voices work with the sync endpoint only.

## Endpoint

`POST https://api.echonova.ai/v1/text-to-speech`

## Authentication

Keys are created in the portal under Developers, sent as `Authorization: Bearer YOUR_API_KEY`. See [Get an API key](/docs/developers/get-an-api-key).

## Request

| Name     | In   | Type                        | Required | Limits                                 | Description                                                                            |
| -------- | ---- | --------------------------- | -------- | -------------------------------------- | -------------------------------------------------------------------------------------- |
| text     | body | string                      | Yes      | 1 to 500 chars                         | The text to speak, 1 to 500 characters. Leading and trailing whitespace is trimmed.    |
| voiceId  | body | string                      | Yes      |                                        | The id of a voice from List available voices. The voice fixes the dialect.             |
| speed    | body | number                      | No       | 0.5 to 2, default 1                    | How fast the voice speaks.                                                             |
| cfg      | body | number                      | No       | 1 to 5, default 2                      | How closely the voice follows the text. Leave it at the default unless you are tuning. |
| numSteps | body | 16 \| 32 \| 48 \| 64 \| 128 | No       | one of 16, 32, 48, 64, 128, default 48 | Diffusion steps. More steps are slower and cleaner.                                    |
| tashkeel | body | boolean                     | No       | default false                          | Adds the vowel marks to Arabic text before it is read. Applies to Fusha voices only.   |

## Responses

| Status | Description           |
| ------ | --------------------- |
| 200    | OK                    |
| 400    | Bad Request           |
| 401    | Unauthorized          |
| 402    | Payment Required      |
| 403    | Forbidden             |
| 404    | Not Found             |
| 429    | Too Many Requests     |
| 500    | Internal Server Error |
| 503    | Service Unavailable   |
| 504    | Gateway Timeout       |

## Response

A success answers 200 with the envelope `{ data, success, statusCode, errorMessage }`. The `data` fields:

| Field  | Type   | Description                                                                                |
| ------ | ------ | ------------------------------------------------------------------------------------------ |
| jobId  | string | The id of the job the call created.                                                        |
| status | string | processing once the request is accepted. Poll the job, or receive a job.completed webhook. |

```json
{
  "data": {
    "jobId": "66e5f0c2a1b2c3d4e5f60718",
    "status": "processing"
  },
  "success": true,
  "statusCode": 200,
  "errorMessage": null
}
```

## Errors

| Status | errorMessage                                                                                                                                             | When                                                                                                                                                                                                                                                                                       | Fix                                                                               |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
| 400    | text must be longer than or equal to 1 characters, text should not be empty                                                                              | text is an empty string or only whitespace.                                                                                                                                                                                                                                                | Send at least one non-whitespace character.                                       |
| 400    | text must be shorter than or equal to 500 characters, text must be longer than or equal to 1 characters, text should not be empty, text must be a string | text is missing or null; every rule on the field fails at once.                                                                                                                                                                                                                            | Send text as a JSON string.                                                       |
| 400    | text must be shorter than or equal to 500 characters, text must be longer than or equal to 1 characters, text must be a string                           | text is a number, a boolean or an object.                                                                                                                                                                                                                                                  | Send text as a JSON string.                                                       |
| 400    | text must be shorter than or equal to 500 characters                                                                                                     | text is over the 500-character limit.                                                                                                                                                                                                                                                      | Split the text into several calls.                                                |
| 400    | voiceId should not be empty                                                                                                                              | voiceId is an empty string.                                                                                                                                                                                                                                                                | Send an id from GET /v1/text-to-speech/voices.                                    |
| 400    | voiceId should not be empty, voiceId must be a string                                                                                                    | voiceId is missing or null.                                                                                                                                                                                                                                                                | Send an id from GET /v1/text-to-speech/voices.                                    |
| 400    | speed must not be less than 0.5                                                                                                                          | speed is below 0.5.                                                                                                                                                                                                                                                                        | Send a speed from 0.5 to 2.                                                       |
| 400    | speed must not be greater than 2                                                                                                                         | speed is above 2.                                                                                                                                                                                                                                                                          | Send a speed from 0.5 to 2.                                                       |
| 400    | speed must not be greater than 2, speed must not be less than 0.5, speed must be a number conforming to the specified constraints                        | speed is not a JSON number, for example the string "1".                                                                                                                                                                                                                                    | Send a number, not a string.                                                      |
| 400    | cfg must not be less than 1                                                                                                                              | cfg is below 1.                                                                                                                                                                                                                                                                            | Send a cfg from 1 to 5.                                                           |
| 400    | cfg must not be greater than 5                                                                                                                           | cfg is above 5.                                                                                                                                                                                                                                                                            | Send a cfg from 1 to 5.                                                           |
| 400    | cfg must not be greater than 5, cfg must not be less than 1, cfg must be a number conforming to the specified constraints                                | cfg is not a JSON number, for example the string "1".                                                                                                                                                                                                                                      | Send a number, not a string.                                                      |
| 400    | numSteps must be one of the following values: 16, 32, 48, 64, 128                                                                                        | numSteps is any other value.                                                                                                                                                                                                                                                               | 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 or query carries a field the endpoint does not define.                                                                                                                                                                                                                            | Remove the field.                                                                 |
| 400    | Invalid identifier                                                                                                                                       | voiceId is not a well-formed id.                                                                                                                                                                                                                                                           | Copy the id exactly as GET /v1/text-to-speech/voices returned it.                 |
| 400    | Cloned voices work with instant generation only for now.                                                                                                 | The voice is a cloned voice, which this endpoint does not support.                                                                                                                                                                                                                         | Use POST /v1/text-to-speech/sync for cloned voices.                               |
| 401    | Unauthorized                                                                                                                                             | The Authorization header is missing or is not Bearer ek\_..., no key matches the token, or the key is deleted, disabled or its account is closed.                                                                                                                                          | Send Authorization: Bearer ek\_... with an enabled key.                           |
| 402    | Insufficient subscription balance                                                                                                                        | The workspace balance cannot cover the charge for this call; nothing ran and nothing was charged.                                                                                                                                                                                          | Add credits to the workspace.                                                     |
| 403    | Subscription not found                                                                                                                                   | The workspace that owns the key has no subscription.                                                                                                                                                                                                                                       | Check the workspace's plan in the portal.                                         |
| 403    | Subscription expired or inactive                                                                                                                         | The subscription is in a state other than active or trial.                                                                                                                                                                                                                                 | Renew the subscription.                                                           |
| 403    | This API key does not have access to Text-to-Speech services                                                                                             | The key is restricted and Text to Speech is not among its services.                                                                                                                                                                                                                        | Edit the key and grant Text to Speech, or use another key.                        |
| 404    | Voice not found                                                                                                                                          | No active voice has this voiceId.                                                                                                                                                                                                                                                          | List the voices again and pick one.                                               |
| 429    | ThrottlerException: Too Many Requests                                                                                                                    | The key made more than 100 requests to one endpoint in 60 seconds, or one IP address made more than 100 requests to one endpoint in 60 seconds (the IP limit is counted before the key is checked, so it can answer with no key at all); a Retry-After header carries the seconds to wait. | Wait Retry-After seconds, then retry.                                             |
| 429    | Monthly spending limit exhausted                                                                                                                         | The call would take the key past its Credit Limit for this billing cycle; nothing ran and there is no Retry-After header.                                                                                                                                                                  | Raise or clear the limit on the key, or wait for the next cycle.                  |
| 500    | Something went wrong                                                                                                                                     | An error the API did not classify.                                                                                                                                                                                                                                                         | Check the request, then retry after a short wait; contact support if it persists. |
| 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.                                                         |
| 503    | Database unavailable                                                                                                                                     | A transient outage inside the API.                                                                                                                                                                                                                                                         | Retry after a short wait.                                                         |
| 504    | Database timed out                                                                                                                                       | A transient timeout inside the API.                                                                                                                                                                                                                                                        | Retry after a short wait.                                                         |

## Code samples

:::tabs

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

```python
import requests

response = requests.post(
    "https://api.echonova.ai/v1/text-to-speech",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={"text":"الطقس اليوم جميل والسماء صافية في المدينة","voiceId":"66e5ef3a9c1d2b4f8a7e6c01","speed":1,"cfg":2,"numSteps":48,"tashkeel":false},
)
print(response.json())
```

```javascript
const response = await fetch('https://api.echonova.ai/v1/text-to-speech', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ text: 'الطقس اليوم جميل والسماء صافية في المدينة', voiceId: '66e5ef3a9c1d2b4f8a7e6c01', speed: 1, cfg: 2, numSteps: 48, tashkeel: false }),
});
console.log(await response.json());
```

:::
