Appearance
Compatible endpoints
Drop-in endpoints for text to speech, forced alignment, speech to text and sound effects. Each takes the file or text in the request and answers when the result is ready, so existing scripts only need a new address and key. If the work outlasts the wait, speech to text, forced alignment and sound effects answer 202 instead: see When the work is still running.
These endpoints accept the same request shapes as the ElevenLabs API.
Everything they make is also an ordinary Cinara generation: charged the same way, listed in History with the key's name, and readable through the matching feature endpoint.
Error responses carry Cinara's fields plus a detail object:
json
{
"error": "invalid_request",
"message": "Send a JSON body with the text to speak",
"detail": { "status": "invalid_request", "message": "Send a JSON body with the text to speak" }
}All four need a verified phone.
Endpoints:
POST /v1/text-to-speech/{voiceId}POST /v1/text-to-speech/{voiceId}/streamPOST /v1/forced-alignmentPOST /v1/speech-to-textPOST /v1/sound-generation
Migrating from ElevenLabs
- Change the base URL to
https://api.cinara.ai. Paths stay the same. - Keep the
xi-api-keyheader, with a Cinara key (cin_…) created on the API Keys page in the Cinara app.Authorization: Bearer cin_…works too. - Change voice ids. ElevenLabs voice ids don't exist in Cinara. Use a Cinara voice id instead:
- Library voices, such as
mentororhaven: listed byGET /v1/speech/options(voices[].id), and in the voice table. - Your workspace's cloned and designed voices:
providerVoiceIdfromGET /v1/voices. - In the app, the Voice Library page shows every voice you can use.
- Library voices, such as
- Model ids. Send
speechorspeech-hdasmodel_id. Any other model id, including ElevenLabs model ids, usesspeech-hd(2 credits per character). - Output format. Text to speech returns MP3 only. Use an
output_formatof the formmp3_<rate>_<kbps>, such asmp3_44100_128. - Fields Cinara reads. Only the fields listed on this page are used; other fields and query parameters are ignored. For example, text to speech reads
voice_settings.speedbut no other voice settings. - Streaming.
/streamreturns the whole MP3 once it's made, not in chunks. - Rate limit. 120 requests a minute per key. See Rate limit.
- User-Agent. Send your own
User-Agentheader, such asmy-app/1.0. See Clients and User-Agent.
python
import os, requests
resp = requests.post(
"https://api.cinara.ai/v1/text-to-speech/mentor",
params={"output_format": "mp3_44100_128"},
headers={"xi-api-key": os.environ["CINARA_API_KEY"], "User-Agent": "my-app/1.0"},
json={"text": "Hello from Cinara.", "model_id": "speech"},
)
resp.raise_for_status()
open("hello.mp3", "wb").write(resp.content)When the work is still running
POST /v1/forced-alignment, POST /v1/speech-to-text and POST /v1/sound-generation wait for their result. If it isn't ready when the wait ends, they return 202 Accepted with the header X-Generation-Id and:
json
{ "status": "processing", "generation_id": "a1d6f3b8-7c2e-4b9a-8e5d-3f0c6a2b9e14" }The work carries on and nothing is refunded. Poll GET /v1/generations/{id} until status isn't running, then read the result from the transcript, alignment or sound effects endpoints.
Text to speech always answers with the audio or an error.
Text to speech
POST /v1/text-to-speech/{voiceId}POST /v1/text-to-speech/{voiceId}/stream
Speaks text in a voice and returns the MP3. Both paths behave the same.
It works like Create speech: the workspace's pronunciation rules apply, long text is split and joined, and the result is a speech generation.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
voiceId | path | yes | A Cinara library voice id, or a workspace voice's providerVoiceId. |
output_format | query | no | mp3_<sample rate>_<kbps>. Default mp3_44100_128. A rate of 44100 or more returns 256 kbps, 44.1 kHz MP3. A lower rate returns 128 kbps MP3. The kbps in the name isn't used. |
Request body
JSON:
| Field | Type | Required | Description |
|---|---|---|---|
text | string | yes | The text. Must not be blank. Up to 50,000 characters. |
model_id | string | no | speech or speech-hd. Any other value, or none, uses speech-hd. |
voice_settings.speed | number | no | Speaking speed. Default 1. Clamped to 0.5–2. |
Response
200 OK with the MP3 bytes and:
| Header | Value |
|---|---|
Content-Type | audio/mpeg |
X-Generation-Id | The speech generation's id |
X-Credits-Remaining | The workspace's balance after the charge |
Errors
| Status | error | Message |
|---|---|---|
| 400 | invalid_request | Cinara returns MP3 here: use an output_format such as mp3_44100_128 |
| 400 | invalid_request | Send a JSON body with the text to speak |
| 400 | invalid_request | Enter some text |
| 400 | invalid_request | Text is limited to 50,000 characters |
| 400 | invalid_request | Unknown voice "aB3dE5fG7hJ9kL2mN4pQ". Use a Cinara voice id such as mentor or haven; GET /v1/speech/options lists them all. |
| 402 | insufficient_credits | This needs 36 credits |
| 403 | phone_unverified | Verify your phone number to start creating. |
| 502 | generation_failed | Speech generation failed. Your credits were refunded. |
Credits
max(1, ceil(characters × rate)): 1 credit per character for speech, 2 for speech-hd (the default). Counted on text as sent. Refunded if it fails.
Example
bash
curl -X POST "https://api.cinara.ai/v1/text-to-speech/mentor?output_format=mp3_44100_128" \
-H "xi-api-key: $CINARA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Hello from Cinara.", "model_id": "speech"}' \
--output hello.mp3Forced alignment
POST /v1/forced-alignment
Times every character and word of a text against its recording.
It runs as an ordinary alignment: charged per minute, listed in History, and exportable with GET /v1/alignments/{id}/export using the X-Generation-Id. If the alignment is still running when the wait ends, you get 202 with its id instead (see When the work is still running). If the request ends early, reading GET /v1/alignments/{id} later still finishes the job.
Request body
multipart/form-data:
| Field | Type | Required | Description |
|---|---|---|---|
file | file | yes | The recording. Up to 50 MB. MP3, WAV, M4A, AAC, OGG, FLAC or WebM, judged by the file name's ending or its type. Up to 10 hours. |
text | string | yes | The words spoken. Up to 100,000 characters. |
Response
200 OK with header X-Generation-Id:
json
{
"characters": [
{ "text": "H", "start": 0.12, "end": 0.18 },
{ "text": "i", "start": 0.18, "end": 0.24 },
{ "text": " ", "start": 0.24, "end": 0.31 },
{ "text": "t", "start": 0.31, "end": 0.358 }
],
"words": [
{ "text": "Hi", "start": 0.12, "end": 0.24, "loss": 0 },
{ "text": "there.", "start": 0.31, "end": 0.6, "loss": 0 }
],
"loss": 0
}| Field | Description |
|---|---|
characters | Every character of text, in order, so they spell the text exactly. A word's time is shared across its letters; spaces and line breaks share the gap between words. |
words | Each space-separated word with its time. loss is 0 when the word was heard and 1 when its time was estimated from its neighbours. |
loss | The mean word loss, 0–1. |
Times are in seconds and never run backwards.
Errors
| Status | error | Message |
|---|---|---|
| 400 | invalid_request | Send the words spoken in the recording as a field named text |
| 400 | invalid_request | Text can be up to 100,000 characters |
| 400 | invalid_request | Send the recording as a multipart file field named file |
| 413 | invalid_request | Recordings sent here can be up to 50 MB |
| 400 | invalid_request | Send an MP3, WAV, M4A, AAC, OGG, FLAC or WebM recording |
| 400 | invalid_request | Recordings can be up to 10 hours |
| 402 | insufficient_credits | This needs 5 credits |
| 403 | phone_unverified | Verify your phone number to start creating. |
| 422 | alignment_failed | None of the text was heard in the recording. Check that the file and the text match. Your credits were refunded. |
| 502 | generation_failed | Alignment failed. Your credits were refunded. |
Credits
max(1, ceil(minutes × 100)). The length is read from the file (MP3, WAV, M4A/MP4) when possible, otherwise estimated from its size; the final charge uses the longer of that and the speech heard. Refunded if nothing is heard or it fails.
Example
bash
curl -X POST https://api.cinara.ai/v1/forced-alignment \
-H "xi-api-key: $CINARA_API_KEY" \
-F file=@hello.mp3 \
-F text="Hello from Cinara."Speech to text
POST /v1/speech-to-text
Transcribes a recording: text, word timings, spacing, sound events and speakers.
It runs as an ordinary transcript: charged per minute, listed in History, and editable and exportable through /v1/transcripts/{id} using the X-Generation-Id. If it's still running when the wait ends, you get 202 with its id instead (see When the work is still running).
Request body
multipart/form-data:
| Field | Type | Required | Description |
|---|---|---|---|
file | file | yes | The recording. Same rules as Forced alignment: up to 50 MB, MP3, WAV, M4A, AAC, OGG, FLAC or WebM. |
language_code | string | no | A 2- or 3-letter code such as en or hi. Omit to detect the language. |
diarize | string | no | true to label speakers. Default: off. |
tag_audio_events | string | no | false to leave out sound events such as [laughs]. Default: on. |
Response
200 OK with header X-Generation-Id:
json
{
"language_code": "eng",
"language_probability": 0.99,
"text": "Thanks for making the time.",
"words": [
{ "text": "Thanks", "start": 0.42, "end": 0.71, "type": "word", "speaker_id": "speaker_0" },
{ "text": " ", "start": 0.71, "end": 0.74, "type": "spacing", "speaker_id": "speaker_0" },
{ "text": "for", "start": 0.74, "end": 0.86, "type": "word", "speaker_id": "speaker_0" }
]
}| Field | Description |
|---|---|
language_code | The detected language. |
language_probability | How sure the detection is, or null. |
words[].type | word, spacing or audio_event. |
words[].speaker_id | The speaker, or null without diarize. |
Errors
| Status | error | Message |
|---|---|---|
| 400 | invalid_request | language_code must be a 2 or 3 letter code, such as en or hi |
| 400 | invalid_request | Send the recording as a multipart file field named file |
| 413 | invalid_request | Recordings sent here can be up to 50 MB |
| 400 | invalid_request | Send an MP3, WAV, M4A, AAC, OGG, FLAC or WebM recording |
| 400 | invalid_request | Recordings can be up to 10 hours |
| 402 | insufficient_credits | This needs 25 credits |
| 403 | phone_unverified | Verify your phone number to start creating. |
| 502 | generation_failed | Transcription failed. Your credits were refunded. |
Credits
max(1, ceil(minutes × 100)), with the same length rules as Forced alignment. Refunded if it fails.
Example
bash
curl -X POST https://api.cinara.ai/v1/speech-to-text \
-H "xi-api-key: $CINARA_API_KEY" \
-F file=@interview.mp3 \
-F diarize=true \
-F language_code=enGenerate a sound effect
POST /v1/sound-generation
Makes one take of a described sound and returns the MP3.
It runs as an ordinary sound effects job with one take. The request waits for the sound (up to about 5 minutes). If it isn't ready by then, you get 202 with its id instead (see When the work is still running).
Request body
JSON:
| Field | Type | Required | Description |
|---|---|---|---|
text | string | yes | The sound. 1–450 characters. |
duration_seconds | number | no | 0.5–22 seconds, rounded to 0.1. Omit or null to fit the length to the sound. |
prompt_influence | number | no | How strictly to follow the text, 0–1. Default 0.3. |
loop | boolean | no | A sound that repeats seamlessly. Default false. |
Response
200 OK with the MP3 and headers Content-Type: audio/mpeg, Content-Disposition: attachment; filename="<slug of text>-sound-1.mp3" and X-Generation-Id.
Errors
| Status | error | Message |
|---|---|---|
| 400 | invalid_request | Send a JSON body with the text describing the sound |
| 400 | invalid_request | Describe the sound |
| 400 | invalid_request | Keep the description under 450 characters |
| 400 | invalid_request | Choose a length from 0.5 to 22 seconds, or automatic |
| 400 | invalid_request | Prompt influence must be between 0 and 1 |
| 402 | insufficient_credits | This needs 100 credits |
| 403 | phone_unverified | Verify your phone number to start creating. |
| 502 | generation_failed | Sound effect failed. Your credits were refunded. |
| 502 | generation_failed | The sound was made but couldn't be sent. Find it in History. |
Credits
The same as sound effects from text with one take: reserved ceil(length × 20) (length 22 when automatic), final ceil(seconds made × 20). Refunded if it fails.
Example
bash
curl -X POST https://api.cinara.ai/v1/sound-generation \
-H "xi-api-key: $CINARA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Footsteps on gravel, slow and steady", "duration_seconds": 5}' \
--output steps.mp3