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.

Base URL
https://api.nexacalling.com/api/v1

Every 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.

Header
Authorization: Bearer vp_live_xxxxxxxxxxxx

The 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.mp3

Text to speech

POST/api/v1/text-to-speechBearer auth
Generates audio from text. Responds with raw audio/mpeg bytes rather than JSON, so write the body straight to a file.

Parameters · JSON body

NameTypeRequiredDescription
textstringRequiredThe text to speak. 1 to 5000 characters. Billed at one unit per character.
voice_idstringRequiredA 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.mp3

Example 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

StatusCodeWhen
400INVALID_REQUESTMissing text, empty text, or over 5000 characters.
404NOT_FOUNDNo such voice in your organization.
401INVALID_API_KEYThe key is missing, malformed, unknown or revoked.
403ACCOUNT_SUSPENDEDThe account or organization is suspended.
429RATE_LIMITEDMore than 100 requests in a minute on this key.
502PROVIDER_ERRORSynthesis failed upstream. Safe to retry.

Streaming text to speech

POST/api/v1/text-to-speech/streamBearer auth
The same synthesis as above, but the audio is streamed back as it is produced rather than buffered into one response — first bytes arrive in a few hundred milliseconds. This is what a real-time voice agent needs. Use format to choose MP3 (default) or raw PCM; the sample rate is returned in the X-Sample-Rate header. Billed per character, exactly like /text-to-speech.

Parameters · JSON body

NameTypeRequiredDescription
textstringRequiredThe text to speak. 1 to 5000 characters. Billed at one unit per character.
voice_idstringRequiredA voice ID from GET /voices.
formatstringOptionalmp3_44100 (default), pcm_16000 or pcm_24000. PCM is raw 16-bit mono, ideal for agent frameworks.

Example request

curl -N -X POST https://api.nexacalling.com/api/v1/text-to-speech/stream \
  -H "Authorization: Bearer $VOICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text":"Streaming, for real-time agents.","voice_id":"voice_xxxxx","format":"mp3_44100"}' \
  --output stream.mp3

Example response

HTTP/1.1 200 OK
Content-Type: audio/mpeg
X-Sample-Rate: 44100
X-Request-ID: req_k3f9x2mQ7Lp0
Transfer-Encoding: chunked

<binary audio data, streamed>

Example error

{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "text must not be empty.",
    "request_id": "req_k3f9x2mQ7Lp0"
  }
}

Errors

StatusCodeWhen
400INVALID_REQUESTMissing text, an unknown format, or over 5000 characters.
404NOT_FOUNDNo such voice in your organization.
401INVALID_API_KEYThe key is missing, malformed, unknown or revoked.
403ACCOUNT_SUSPENDEDThe account or organization is suspended.
429RATE_LIMITEDMore than 100 requests in a minute on this key.
502PROVIDER_ERRORSynthesis failed upstream. Safe to retry.

Speech to text

POST/api/v1/speech-to-textBearer auth
Transcribes an uploaded audio file. Synchronous — the transcript comes back on the same request, with no job to poll.

Parameters · multipart/form-data

NameTypeRequiredDescription
filefileRequiredmp3, wav, m4a, webm or ogg, up to 25 MB. Validated by content, not just extension.
languagestringOptionalISO 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

StatusCodeWhen
400INVALID_REQUESTNo file was supplied, or the language code is invalid.
413PAYLOAD_TOO_LARGEThe file is larger than 25 MB.
415UNSUPPORTED_MEDIA_TYPEThe file is not mp3, wav, m4a, webm or ogg.
502PROVIDER_ERRORTranscription failed upstream. Safe to retry.
401INVALID_API_KEYThe key is missing, malformed, unknown or revoked.
403ACCOUNT_SUSPENDEDThe account or organization is suspended.
429RATE_LIMITEDMore than 100 requests in a minute on this key.

Speech recognition

POST/api/v1/asrBearer auth
The speech-recognition surface. It accepts the same inputs and returns the same shape as speech-to-text; usage is recorded under ASR so you can bill and report on recognition workloads separately.

Parameters · multipart/form-data

NameTypeRequiredDescription
filefileRequiredmp3, wav, m4a, webm or ogg, up to 25 MB. Validated by content, not just extension.
languagestringOptionalISO 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

StatusCodeWhen
400INVALID_REQUESTNo file was supplied, or the language code is invalid.
413PAYLOAD_TOO_LARGEThe file is larger than 25 MB.
415UNSUPPORTED_MEDIA_TYPEThe file is not mp3, wav, m4a, webm or ogg.
502PROVIDER_ERRORTranscription failed upstream. Safe to retry.
401INVALID_API_KEYThe key is missing, malformed, unknown or revoked.
403ACCOUNT_SUSPENDEDThe account or organization is suspended.
429RATE_LIMITEDMore than 100 requests in a minute on this key.

List voices

GET/api/v1/voicesBearer auth
Returns the shared catalogue plus any voices your organization has cloned. Another organization's voices are never included.

Example 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",
      "preview_available": true,
      "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

StatusCodeWhen
401INVALID_API_KEYThe key is missing, malformed, unknown or revoked.
403ACCOUNT_SUSPENDEDThe account or organization is suspended.
429RATE_LIMITEDMore than 100 requests in a minute on this key.

Play a sample

GET/api/v1/voices/{id}/previewBearer auth
Returns a short recorded sample of the voice as raw audio/mpeg bytes, so you can audition it before using it. This is a recording, not synthesis: nothing is generated and nothing is billed. Only voices listed with preview_available: true have one.

Example request

curl https://api.nexacalling.com/api/v1/voices/voice_xxxxx/preview \
  -H "Authorization: Bearer $VOICE_API_KEY" \
  --output preview.mp3

Example response

HTTP/1.1 200 OK
Content-Type: audio/mpeg
X-Request-ID: req_k3f9x2mQ7Lp0

<binary audio data>

Example error

{
  "error": {
    "code": "NOT_FOUND",
    "message": "No preview is available for this voice.",
    "request_id": "req_k3f9x2mQ7Lp0"
  }
}

Errors

StatusCodeWhen
404NOT_FOUNDNo such voice in your organization, or the voice has no sample.
401INVALID_API_KEYThe key is missing, malformed, unknown or revoked.
403ACCOUNT_SUSPENDEDThe account or organization is suspended.
429RATE_LIMITEDMore than 100 requests in a minute on this key.
502PROVIDER_ERRORThe sample could not be fetched. Retry, or contact support with the request ID.

Clone a voice

POST/api/v1/voices/cloneBearer auth
Creates an instant voice clone from an audio sample. consent is required and must be true: 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

NameTypeRequiredDescription
namestringRequiredA name for the voice. Up to 64 characters.
consentstringRequiredMust be 'true'. Confirms you are authorized to clone this voice.
filefileRequiredA clear sample of a single speaker. mp3, wav, m4a, webm or ogg, up to 25 MB.
descriptionstringOptionalOptional 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

StatusCodeWhen
400INVALID_REQUESTMissing name, missing consent, or the clone limit is reached.
403SERVICE_DISABLEDVoice cloning is disabled for this account.
413PAYLOAD_TOO_LARGEThe file is larger than 25 MB.
415UNSUPPORTED_MEDIA_TYPEThe file is not mp3, wav, m4a, webm or ogg.
502PROVIDER_ERRORTranscription failed upstream. Safe to retry.
401INVALID_API_KEYThe key is missing, malformed, unknown or revoked.
403ACCOUNT_SUSPENDEDThe account or organization is suspended.
429RATE_LIMITEDMore than 100 requests in a minute on this key.

Delete a voice

DELETE/api/v1/voices/{id}Bearer auth
Deletes one of your organization's cloned voices. Catalogue voices are shared and cannot be deleted.

Example 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

StatusCodeWhen
404NOT_FOUNDNo such cloned voice in your organization.
401INVALID_API_KEYThe key is missing, malformed, unknown or revoked.
403ACCOUNT_SUSPENDEDThe account or organization is suspended.
429RATE_LIMITEDMore than 100 requests in a minute on this key.

Build a voice agent

The streaming endpoint is built for real-time agents. Our nexavoice-livekit plugin wires it into LiveKit Agents so your agent speaks with one of your voices — you write the agent, we are the voice engine inside it. Install the plugin alongside LiveKit Agents:

Install
pip install nexavoice-livekit "livekit-agents[silero]"

Point it at your key, then drop the TTS into an AgentSession. The plugin calls our API — your agent code never names an upstream provider.

Environment
export NEXAVOICE_API_KEY=vp_live_xxxxxxxxxxxx
agent.py
from livekit.agents import Agent, AgentSession
from livekit.plugins import openai, silero

from nexavoice_livekit import TTS  # reads NEXAVOICE_API_KEY from the environment

session = AgentSession(
    stt=openai.STT(),
    llm=openai.LLM(model="gpt-4o-mini"),
    tts=TTS(voice_id="voice_xxxxx"),   # one of your voices from GET /voices
    vad=silero.VAD.load(),
)
# session.start(agent=Agent(instructions="You are a helpful assistant."), room=ctx.room)

The plugin streams audio over plain HTTP, so there is nothing new to host. A complete, runnable agent and the plugin source are in plugins/nexavoice-livekit. For the full design — including how the voice engine stays swappable behind this contract — see docs/voice-agents.md.

Usage

Every request is recorded. Billable work also records units. Failed requests are logged but never billed.

EndpointUnit
/text-to-speechOne per character of text submitted
/text-to-speech/streamOne per character of text submitted
/speech-to-textOne per second of audio, rounded up
/asrOne per second of audio, rounded up
/voices/cloneOne 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"
  }
}
StatusCodeMeaning
400INVALID_REQUESTThe request failed validation.
401INVALID_API_KEYThe key is missing, malformed, unknown or revoked.
403ACCOUNT_SUSPENDEDThe account or organization is suspended.
403SERVICE_DISABLEDAn administrator has disabled this service.
404NOT_FOUNDThe resource does not exist, or is not yours.
413PAYLOAD_TOO_LARGEThe upload exceeds the size cap.
415UNSUPPORTED_MEDIA_TYPEThe file is not an accepted audio format.
429RATE_LIMITEDYou exceeded the per-key rate limit.
500INTERNAL_ERRORSomething failed on our side.
502PROVIDER_ERRORThe voice service failed. Safe to retry.

A resource owned by another organization returns 404, not 403 — a 403 would confirm the ID exists.