API documentation
One API for text to speech, speech to text, speech recognition and voice cloning. All endpoints live under a single base URL and share one bearer token.
https://api.nexacalling.com/api/v1Every response carries an X-Request-ID header, and every error repeats it in the body. It identifies one row in your request log — quote it when contacting support.
Authentication
Create a key on the API keys page and send it as a bearer token.
Authorization: Bearer vp_live_xxxxxxxxxxxxThe secret is shown once, at creation. We store only a prefix and a hash, so it cannot be recovered — if you lose it, revoke the key and create another. Keys are scoped to one organization and can only ever see that organization’s voices, usage and logs.
Quick start
List your voices, then synthesize speech with one. Set VOICE_API_KEY in your environment first.
# 1. List the voices available to you
curl https://api.nexacalling.com/api/v1/voices -H "Authorization: Bearer $VOICE_API_KEY"
# 2. Synthesize speech with one of them
curl -X POST https://api.nexacalling.com/api/v1/text-to-speech \
-H "Authorization: Bearer $VOICE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"My first request.","voice_id":"voice_xxxxx"}' \
--output speech.mp3Text to speech
/api/v1/text-to-speechBearer authParameters · JSON body
| Name | Type | Required | Description |
|---|---|---|---|
| text | string | Required | The text to speak. 1 to 5000 characters. Billed at one unit per character. |
| voice_id | string | Required | A voice ID from GET /voices. Must belong to your organization or the shared catalogue. |
Example request
curl -X POST https://api.nexacalling.com/api/v1/text-to-speech \
-H "Authorization: Bearer $VOICE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Hello from the Voice API Platform.","voice_id":"voice_xxxxx"}' \
--output speech.mp3Example response
HTTP/1.1 200 OK
Content-Type: audio/mpeg
X-Request-ID: req_k3f9x2mQ7Lp0
<binary audio data>Example error
{
"error": {
"code": "INVALID_REQUEST",
"message": "text must not be empty.",
"request_id": "req_k3f9x2mQ7Lp0"
}
}Errors
| Status | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST | Missing text, empty text, or over 5000 characters. |
| 404 | NOT_FOUND | No such voice in your organization. |
| 401 | INVALID_API_KEY | The key is missing, malformed, unknown or revoked. |
| 403 | ACCOUNT_SUSPENDED | The account or organization is suspended. |
| 429 | RATE_LIMITED | More than 100 requests in a minute on this key. |
| 502 | PROVIDER_ERROR | Synthesis failed upstream. Safe to retry. |
Speech to text
/api/v1/speech-to-textBearer authParameters · multipart/form-data
| Name | Type | Required | Description |
|---|---|---|---|
| file | file | Required | mp3, wav, m4a, webm or ogg, up to 25 MB. Validated by content, not just extension. |
| language | string | Optional | ISO language hint such as 'en'. Omit to detect automatically. |
Example request
curl -X POST https://api.nexacalling.com/api/v1/speech-to-text \
-H "Authorization: Bearer $VOICE_API_KEY" \
-F "file=@recording.mp3" \
-F "language=en"Example response
{
"text": "Hello, and welcome to the Voice API Platform.",
"language": "en",
"request_id": "req_k3f9x2mQ7Lp0"
}Example error
{
"error": {
"code": "UNSUPPORTED_MEDIA_TYPE",
"message": "That file does not appear to be audio. Accepted formats: mp3, wav, m4a, webm, ogg.",
"request_id": "req_k3f9x2mQ7Lp0"
}
}Errors
| Status | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST | No file was supplied, or the language code is invalid. |
| 413 | PAYLOAD_TOO_LARGE | The file is larger than 25 MB. |
| 415 | UNSUPPORTED_MEDIA_TYPE | The file is not mp3, wav, m4a, webm or ogg. |
| 502 | PROVIDER_ERROR | Transcription failed upstream. Safe to retry. |
| 401 | INVALID_API_KEY | The key is missing, malformed, unknown or revoked. |
| 403 | ACCOUNT_SUSPENDED | The account or organization is suspended. |
| 429 | RATE_LIMITED | More than 100 requests in a minute on this key. |
Speech recognition
/api/v1/asrBearer authParameters · multipart/form-data
| Name | Type | Required | Description |
|---|---|---|---|
| file | file | Required | mp3, wav, m4a, webm or ogg, up to 25 MB. Validated by content, not just extension. |
| language | string | Optional | ISO language hint such as 'en'. Omit to detect automatically. |
Example request
curl -X POST https://api.nexacalling.com/api/v1/asr \
-H "Authorization: Bearer $VOICE_API_KEY" \
-F "file=@recording.mp3" \
-F "language=en"Example response
{
"text": "Hello, and welcome to the Voice API Platform.",
"language": "en",
"request_id": "req_k3f9x2mQ7Lp0"
}Example error
{
"error": {
"code": "PAYLOAD_TOO_LARGE",
"message": "Audio must be 25 MB or smaller.",
"request_id": "req_k3f9x2mQ7Lp0"
}
}Errors
| Status | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST | No file was supplied, or the language code is invalid. |
| 413 | PAYLOAD_TOO_LARGE | The file is larger than 25 MB. |
| 415 | UNSUPPORTED_MEDIA_TYPE | The file is not mp3, wav, m4a, webm or ogg. |
| 502 | PROVIDER_ERROR | Transcription failed upstream. Safe to retry. |
| 401 | INVALID_API_KEY | The key is missing, malformed, unknown or revoked. |
| 403 | ACCOUNT_SUSPENDED | The account or organization is suspended. |
| 429 | RATE_LIMITED | More than 100 requests in a minute on this key. |
List voices
/api/v1/voicesBearer authExample request
curl https://api.nexacalling.com/api/v1/voices \
-H "Authorization: Bearer $VOICE_API_KEY"Example response
{
"voices": [
{
"id": "voice_c1x2n8fk4p",
"name": "Narrator",
"language": "en",
"type": "PROVIDER",
"created_at": "2026-08-30T12:00:00.000Z"
}
]
}Example error
{
"error": {
"code": "INVALID_API_KEY",
"message": "Invalid or revoked API key.",
"request_id": "req_k3f9x2mQ7Lp0"
}
}Errors
| Status | Code | When |
|---|---|---|
| 401 | INVALID_API_KEY | The key is missing, malformed, unknown or revoked. |
| 403 | ACCOUNT_SUSPENDED | The account or organization is suspended. |
| 429 | RATE_LIMITED | More than 100 requests in a minute on this key. |
Clone a voice
/api/v1/voices/cloneBearer authtrue: by sending it you confirm you have permission and authorization to use the recording for voice cloning. The confirmation is stored with a timestamp against the voice.Parameters · multipart/form-data
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Required | A name for the voice. Up to 64 characters. |
| consent | string | Required | Must be 'true'. Confirms you are authorized to clone this voice. |
| file | file | Required | A clear sample of a single speaker. mp3, wav, m4a, webm or ogg, up to 25 MB. |
| description | string | Optional | Optional note, up to 500 characters. |
Example request
curl -X POST https://api.nexacalling.com/api/v1/voices/clone \
-H "Authorization: Bearer $VOICE_API_KEY" \
-F "name=Narrator" \
-F "consent=true" \
-F "file=@sample.mp3"Example response
{
"voice": {
"id": "voice_9k2m4x7qab",
"name": "Narrator",
"language": null,
"type": "CLONED",
"created_at": "2026-08-30T12:00:00.000Z"
}
}Example error
{
"error": {
"code": "INVALID_REQUEST",
"message": "consent: consent must be true. You must confirm you are authorized to clone this voice.",
"request_id": "req_k3f9x2mQ7Lp0"
}
}Errors
| Status | Code | When |
|---|---|---|
| 400 | INVALID_REQUEST | Missing name, missing consent, or the clone limit is reached. |
| 403 | SERVICE_DISABLED | Voice cloning is disabled for this account. |
| 413 | PAYLOAD_TOO_LARGE | The file is larger than 25 MB. |
| 415 | UNSUPPORTED_MEDIA_TYPE | The file is not mp3, wav, m4a, webm or ogg. |
| 502 | PROVIDER_ERROR | Transcription failed upstream. Safe to retry. |
| 401 | INVALID_API_KEY | The key is missing, malformed, unknown or revoked. |
| 403 | ACCOUNT_SUSPENDED | The account or organization is suspended. |
| 429 | RATE_LIMITED | More than 100 requests in a minute on this key. |
Delete a voice
/api/v1/voices/{id}Bearer authExample request
curl -X DELETE https://api.nexacalling.com/api/v1/voices/voice_xxxxx \
-H "Authorization: Bearer $VOICE_API_KEY"Example response
{ "deleted": true, "id": "voice_9k2m4x7qab" }Example error
{
"error": {
"code": "NOT_FOUND",
"message": "Voice not found.",
"request_id": "req_k3f9x2mQ7Lp0"
}
}Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND | No such cloned voice in your organization. |
| 401 | INVALID_API_KEY | The key is missing, malformed, unknown or revoked. |
| 403 | ACCOUNT_SUSPENDED | The account or organization is suspended. |
| 429 | RATE_LIMITED | More than 100 requests in a minute on this key. |
Usage
Every request is recorded. Billable work also records units. Failed requests are logged but never billed.
| Endpoint | Unit |
|---|---|
| /text-to-speech | One per character of text submitted |
| /speech-to-text | One per second of audio, rounded up |
| /asr | One per second of audio, rounded up |
| /voices/clone | One per voice created |
Where audio duration cannot be determined, one unit is recorded rather than zero. Your usage and request history are on the usage and requests pages.
Errors
Every error uses one envelope, whatever the status code.
{
"error": {
"code": "INVALID_REQUEST",
"message": "text must not be empty.",
"request_id": "req_k3f9x2mQ7Lp0"
}
}| Status | Code | Meaning |
|---|---|---|
| 400 | INVALID_REQUEST | The request failed validation. |
| 401 | INVALID_API_KEY | The key is missing, malformed, unknown or revoked. |
| 403 | ACCOUNT_SUSPENDED | The account or organization is suspended. |
| 403 | SERVICE_DISABLED | An administrator has disabled this service. |
| 404 | NOT_FOUND | The resource does not exist, or is not yours. |
| 413 | PAYLOAD_TOO_LARGE | The upload exceeds the size cap. |
| 415 | UNSUPPORTED_MEDIA_TYPE | The file is not an accepted audio format. |
| 429 | RATE_LIMITED | You exceeded the per-key rate limit. |
| 500 | INTERNAL_ERROR | Something failed on our side. |
| 502 | PROVIDER_ERROR | The voice service failed. Safe to retry. |
A resource owned by another organization returns 404, not 403 — a 403 would confirm the ID exists.