Appearance
Transcription
Two kinds of speech-to-text job:
- Transcripts turn a recording into an editable transcript with speakers, word timings and sound events.
- Alignments time a script you already have against its recording, word by word, for captions and lyrics.
Both run in the background on an uploaded audio or video file, up to 10 hours long.
For drop-in endpoints that take the file in the request and answer when done, see POST /v1/speech-to-text and POST /v1/forced-alignment. Jobs made there can also be read and exported here.
Endpoints:
GET /v1/transcribe/optionsPOST /v1/transcriptsGET /v1/transcripts/{id}PATCH /v1/transcripts/{id}POST /v1/transcripts/{id}/revertGET /v1/transcripts/{id}/exportPOST /v1/alignmentsGET /v1/alignments/{id}GET /v1/alignments/{id}/export
Get transcription options
GET /v1/transcribe/options
Response
200 OK
json
{
"models": [{ "id": "transcribe", "label": "Cinara Transcribe", "creditsPerMinute": 100 }],
"languages": [
{ "code": "en", "label": "English" },
{ "code": "hi", "label": "Hindi" },
{ "code": "te", "label": "Telugu" }
],
"maxKeyterms": 50,
"maxKeytermChars": 50,
"maxUploadBytes": 2147483648,
"partBytes": 10485760,
"maxSeconds": 36000,
"maxScriptChars": 100000
}languages lists the named languages (shortened above): en, hi, te, ta, kn, ml, mr, bn, gu, pa, ur, ar, es, pt, fr, de, it, nl, pl, tr, ru, uk, zh, ja, ko, id, ms, fil, vi, th, sw. Other 2- or 3-letter ISO 639 codes are accepted too.
Credits
Free.
Example
bash
curl https://api.cinara.ai/v1/transcribe/options \
-H "Authorization: Bearer $CINARA_API_KEY"Create a transcript
POST /v1/transcripts
Starts transcribing an uploaded audio or video file.
Needs a verified phone.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
key | string | yes | The storage key of a finished audio or video upload. |
fileName | string | no | A display name, up to 200 characters. Defaults to the upload's file name. |
model | string | no | transcribe (default). |
language | string | no | A 2- or 3-letter lowercase ISO 639 code, or auto (default) to detect it. |
diarize | boolean | no | Label who is speaking. Default true. |
tagAudioEvents | boolean | no | Tag sounds such as [laughs]. Default true. |
keyterms | string[] | no | Names and jargon to expect. Up to 50 terms, each 1–50 characters. Duplicates are removed. |
durationSeconds | number | no | The file's length in seconds, up to 36,000. When missing, it's estimated from the file size. |
Response
201 Created
json
{
"generation": {
"id": "2d9b6e1f-8a3c-4f7d-b5e2-1c0a7d4f9b83",
"module": "transcribe",
"status": "running",
"input": {
"key": "workspaces/3f6b2a1c-8d4e-4b7a-9c2d-5e1f0a9b8c7d/uploads/5f3a8c1e-2d7b-4e9a-a6c4-8b1e0d3f7c25/interview.mp3",
"fileName": "interview.mp3",
"model": "transcribe",
"language": "en",
"diarize": true,
"tagAudioEvents": true,
"keyterms": ["Cinara", "Hyderabad"],
"contentType": "audio/mpeg",
"size": 12001280,
"durationSeconds": 750,
"durationMeasured": true
},
"output": { "stage": "queued" },
"credits": 1250,
"error": null,
"created_at": "2026-09-15T17:00:02.664918+00:00",
"completed_at": null
},
"credits": 45472
}input.durationMeasured is false when the length was estimated from the file size.
Errors
| Status | error | Message |
|---|---|---|
| 400 | invalid_request | Upload a file first |
| 400 | invalid_request | Unknown transcription model |
| 400 | invalid_request | Recordings can be up to 10 hours |
| 400 | invalid_request | Unsupported language |
| 400 | invalid_request | Use up to 50 key terms |
| 400 | invalid_request | Key terms need 1 to 50 characters each |
| 404 | not_found | That upload isn't finished. Upload the file again. |
| 400 | invalid_request | Upload an audio or video file |
| 402 | insufficient_credits | This needs 1,250 credits |
| 403 | phone_unverified | Verify your phone number to start creating. |
| 502 | generation_failed | Transcription couldn't start. Your credits were refunded. |
Credits
max(1, ceil(minutes × 100)), where minutes is seconds ÷ 60.
- Reserved on
durationSeconds, or on the estimate from file size. - Final on the billed length: with
durationSeconds, the longer of that and the speech heard; with an estimate, the speech heard (at least 1 second). - The difference is refunded, or the extra charged. Refunded in full if it fails.
Example
bash
curl -X POST https://api.cinara.ai/v1/transcripts \
-H "Authorization: Bearer $CINARA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"key": "workspaces/3f6b2a1c-8d4e-4b7a-9c2d-5e1f0a9b8c7d/uploads/5f3a8c1e-2d7b-4e9a-a6c4-8b1e0d3f7c25/interview.mp3", "language": "en", "keyterms": ["Cinara", "Hyderabad"], "durationSeconds": 750}'Get a transcript
GET /v1/transcripts/{id}
Checks a transcription job, moves it forward, and returns it with the transcript once finished.
Response
200 OK
json
{
"generation": {
"id": "2d9b6e1f-8a3c-4f7d-b5e2-1c0a7d4f9b83",
"module": "transcribe",
"status": "succeeded",
"input": {
"key": "workspaces/3f6b2a1c-8d4e-4b7a-9c2d-5e1f0a9b8c7d/uploads/5f3a8c1e-2d7b-4e9a-a6c4-8b1e0d3f7c25/interview.mp3",
"fileName": "interview.mp3",
"model": "transcribe",
"language": "en",
"diarize": true,
"tagAudioEvents": true,
"keyterms": ["Cinara", "Hyderabad"],
"contentType": "audio/mpeg",
"size": 12001280,
"durationSeconds": 750,
"durationMeasured": true
},
"output": {
"language": "eng",
"durationSeconds": 750,
"speakers": 2,
"words": 1842,
"preview": "Thanks for making the time. So tell me how the idea started."
},
"credits": 1250,
"error": null,
"created_at": "2026-09-15T17:00:02.664918+00:00",
"completed_at": "2026-09-15T17:00:58.310247+00:00"
},
"transcript": {
"language": "eng",
"durationSeconds": 750,
"speakers": { "speaker_0": "Speaker 1", "speaker_1": "Speaker 2" },
"segments": [
{
"speaker": "speaker_0",
"start": 0.42,
"end": 3.96,
"tokens": [
{ "text": "Thanks", "start": 0.42, "end": 0.71, "kind": "word", "speaker": "speaker_0", "space": false },
{ "text": "for", "start": 0.74, "end": 0.86, "kind": "word", "speaker": "speaker_0", "space": true },
{ "text": "[laughs]", "start": 3.1, "end": 3.96, "kind": "event", "speaker": "speaker_0", "space": true }
]
}
]
},
"mediaUrl": "https://api.cinara.ai/media/1789535998/Tn5eY…/workspaces/…/uploads/…/interview.mp3",
"credits": 45472
}| Field | Description |
|---|---|
output | Once finished: detected language, durationSeconds, number of speakers, number of words, and a preview of the first paragraph. While running: stage is queued or transcribing. |
transcript | null until the job succeeds. speakers maps speaker ids to display names. segments are paragraphs; a new one starts when the speaker changes, after a 2-second silence, or at a sentence end once a paragraph is long. |
transcript.segments[].tokens[] | kind is word or event. space says whether a space comes before the token (none between words in languages such as Japanese). Times are in seconds. |
mediaUrl | A signed link (6 hours) to the uploaded file, for a player. |
credits | The new balance. Present only on the read where the status changed. |
Failures: Transcription took too long and was stopped., This file couldn't be read. Try MP3, WAV, M4A, MP4 or WebM., Transcription failed.
Errors
| Status | error | Message |
|---|---|---|
| 404 | not_found | Transcript not found |
Credits
Free to read.
Polling example
bash
while :; do
RES=$(curl -s "https://api.cinara.ai/v1/transcripts/$ID" -H "Authorization: Bearer $CINARA_API_KEY")
[ "$(echo "$RES" | jq -r .generation.status)" != "running" ] && break
sleep 5
done
echo "$RES" | jq '{status: .generation.status, error: .generation.error, summary: .generation.output}'Edit a transcript
PATCH /v1/transcripts/{id}
Renames speakers, and changes a paragraph's text or speaker. Words that still match keep their timings; new words share the time around them.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
speakers | object | no | Speaker id → new display name (1–40 characters). Ids must exist in the transcript. |
segments | object[] | no | Up to 1,000 edits. |
segments[].index | integer | yes | The paragraph's position in transcript.segments, starting at 0. |
segments[].text | string | no | New text. Longer than 20,000 characters is cut. |
segments[].speaker | string or null | no | A speaker id from the transcript, or null for no speaker. |
Edits apply in order. Paragraphs left with no words are removed after all edits, so later indexes in the next request can shift.
Response
200 OK
json
{
"generation": {
"id": "2d9b6e1f-8a3c-4f7d-b5e2-1c0a7d4f9b83",
"module": "transcribe",
"status": "succeeded",
"input": { "key": "workspaces/…/uploads/…/interview.mp3", "fileName": "interview.mp3", "…": "…" },
"output": { "language": "eng", "durationSeconds": 750, "speakers": 2, "words": 1843, "preview": "Thanks for making the time. So tell me how the idea started." },
"credits": 1250,
"error": null,
"created_at": "2026-09-15T17:00:02.664918+00:00",
"completed_at": "2026-09-15T17:00:58.310247+00:00"
},
"transcript": {
"language": "eng",
"durationSeconds": 750,
"speakers": { "speaker_0": "Meera", "speaker_1": "Arjun" },
"segments": []
}
}The full updated transcript is returned (segments shortened above).
Errors
| Status | error | Message |
|---|---|---|
| 404 | not_found | Transcript not found |
| 400 | invalid_request | speakers must be an object |
| 400 | invalid_request | segments must be a list of up to 1,000 edits |
| 400 | invalid_request | Unknown speaker speaker_7 |
| 400 | invalid_request | Speaker names need 1 to 40 characters |
| 400 | invalid_request | Segment 12 doesn't exist |
Credits
Free.
Example
bash
curl -X PATCH "https://api.cinara.ai/v1/transcripts/2d9b6e1f-8a3c-4f7d-b5e2-1c0a7d4f9b83" \
-H "Authorization: Bearer $CINARA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"speakers": {"speaker_0": "Meera", "speaker_1": "Arjun"}, "segments": [{"index": 0, "text": "Thanks for making the time today."}]}'Revert a transcript
POST /v1/transcripts/{id}/revert
Discards all edits and restores the transcript as first made. No request body.
Response
200 OK with the same shape as Edit a transcript: { "generation": {…}, "transcript": {…} }.
Errors
| Status | error | Message |
|---|---|---|
| 404 | not_found | Transcript not found |
Credits
Free.
Example
bash
curl -X POST "https://api.cinara.ai/v1/transcripts/2d9b6e1f-8a3c-4f7d-b5e2-1c0a7d4f9b83/revert" \
-H "Authorization: Bearer $CINARA_API_KEY"Export a transcript
GET /v1/transcripts/{id}/export
Downloads a finished transcript as text, subtitles, JSON or a Word document.
Query parameters
| Name | Default | Description |
|---|---|---|
format | txt | txt, srt, vtt, json or docx. |
speakers | on | 0 hides speaker names. |
timestamps | off | 1 shows each paragraph's start time (txt, docx). |
clean | off | 1 leaves out filler words and sound events. |
Response
200 OK with Content-Disposition: attachment; filename="<file name>.<format>" and:
format | Content-Type |
|---|---|
txt | text/plain; charset=utf-8 |
srt | application/x-subrip; charset=utf-8 |
vtt | text/vtt; charset=utf-8 |
json | application/json; charset=utf-8 |
docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
The json export has this shape:
json
{
"language": "eng",
"durationSeconds": 750,
"segments": [
{
"speaker": "Meera",
"start": 0.42,
"end": 3.96,
"text": "Thanks for making the time today.",
"words": [
{ "text": "Thanks", "start": 0.42, "end": 0.71, "type": "word" }
]
}
]
}Subtitles show the speaker's name at the start of each paragraph unless speakers=0.
Errors
| Status | error | Message |
|---|---|---|
| 400 | invalid_request | Choose txt, srt, vtt, json or docx |
| 404 | not_found | Transcript not found |
Credits
Free.
Example
bash
curl "https://api.cinara.ai/v1/transcripts/2d9b6e1f-8a3c-4f7d-b5e2-1c0a7d4f9b83/export?format=srt&speakers=0" \
-H "Authorization: Bearer $CINARA_API_KEY" -o interview.srtCreate an alignment
POST /v1/alignments
Starts timing a script against its recording. The captions show the script exactly as written.
Needs a verified phone.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
key | string | yes | The storage key of a finished audio or video upload. |
script | string | yes | The words spoken. 1–100,000 characters. Line breaks are kept: each line becomes a caption or lyric line. Lines over 240 characters are split at sentence ends. |
fileName | string | no | Up to 200 characters. Defaults to the upload's file name. |
model | string | no | transcribe (default). |
language | string | no | A 2- or 3-letter lowercase ISO 639 code, or auto (default). |
keyterms | string[] | no | Names and jargon. Names found in the script are added automatically; the total is capped at 50. |
durationSeconds | number | no | The file's length in seconds, up to 36,000. When missing, it's estimated from the file size. |
Response
201 Created
json
{
"generation": {
"id": "b8e2f5a1-6c4d-4b9e-a7f3-2d0c9e5b1a46",
"module": "alignment",
"status": "running",
"input": {
"key": "workspaces/3f6b2a1c-8d4e-4b7a-9c2d-5e1f0a9b8c7d/uploads/9c4e2a7b-3f1d-4c8e-b5a9-0e6d2f8b4c17/lesson-3.mp3",
"fileName": "lesson-3.mp3",
"model": "transcribe",
"language": "auto",
"diarize": false,
"tagAudioEvents": false,
"keyterms": ["Cinara"],
"contentType": "audio/mpeg",
"size": 2310144,
"durationSeconds": 144.4,
"durationMeasured": true,
"scriptChars": 1920,
"scriptPreview": "Welcome back. In this lesson we look at how Cinara times captions."
},
"output": { "stage": "queued" },
"credits": 241,
"error": null,
"created_at": "2026-09-15T17:30:40.009113+00:00",
"completed_at": null
},
"credits": 45231
}Errors
| Status | error | Message |
|---|---|---|
| 400 | invalid_request | Upload a file first |
| 400 | invalid_request | Unknown transcription model |
| 400 | invalid_request | Recordings can be up to 10 hours |
| 400 | invalid_request | Paste the script to align |
| 400 | invalid_request | Scripts can be up to 100,000 characters |
| 400 | invalid_request | Unsupported language |
| 400 | invalid_request | Key terms need 1 to 50 characters each |
| 404 | not_found | That upload isn't finished. Upload the file again. |
| 400 | invalid_request | Upload an audio or video file |
| 402 | insufficient_credits | This needs 241 credits |
| 403 | phone_unverified | Verify your phone number to start creating. |
| 502 | generation_failed | Alignment couldn't start. Your credits were refunded. |
Credits
The same rule as Create a transcript: max(1, ceil(minutes × 100)), reserved on the given or estimated length and settled to the billed length.
Example
bash
curl -X POST https://api.cinara.ai/v1/alignments \
-H "Authorization: Bearer $CINARA_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg key "$KEY" --rawfile script lesson-3.txt '{key: $key, script: $script, durationSeconds: 144.4}')"Get an alignment
GET /v1/alignments/{id}
Checks an alignment job, moves it forward, and returns it with the timed script once finished.
Response
200 OK
json
{
"generation": {
"id": "b8e2f5a1-6c4d-4b9e-a7f3-2d0c9e5b1a46",
"module": "alignment",
"status": "succeeded",
"input": { "key": "workspaces/…/uploads/…/lesson-3.mp3", "fileName": "lesson-3.mp3", "scriptChars": 1920, "…": "…" },
"output": {
"language": "eng",
"durationSeconds": 144.4,
"coverage": 0.987,
"lines": 18,
"words": 322,
"preview": "Welcome back. In this lesson we look at how Cinara times captions."
},
"credits": 241,
"error": null,
"created_at": "2026-09-15T17:30:40.009113+00:00",
"completed_at": "2026-09-15T17:30:49.772506+00:00"
},
"alignment": {
"language": "eng",
"durationSeconds": 144.4,
"coverage": 0.987,
"lines": [
{
"text": "Welcome back. In this lesson we look at how Cinara times captions.",
"start": 0.36,
"end": 4.12,
"words": [
{ "text": "Welcome", "start": 0.36, "end": 0.74, "heard": true },
{ "text": "back.", "start": 0.78, "end": 1.1, "heard": true }
]
}
]
},
"mediaUrl": "https://api.cinara.ai/media/1789537849/pQ7vD…/workspaces/…/uploads/…/lesson-3.mp3",
"credits": 45231
}| Field | Description |
|---|---|
alignment | null until the job succeeds. |
alignment.coverage | Share of script words heard in the audio, 0–1. |
alignment.lines[].words[].heard | false when the word wasn't heard and its time was estimated from its neighbours. |
mediaUrl | A signed link (6 hours) to the uploaded file. |
Failures: Alignment took too long and was stopped., The script for this alignment is missing., This file couldn't be read. Try MP3, WAV, M4A, MP4 or WebM., Alignment failed.
Errors
| Status | error | Message |
|---|---|---|
| 404 | not_found | Alignment not found |
Credits
Free to read.
Polling example
bash
while :; do
RES=$(curl -s "https://api.cinara.ai/v1/alignments/$ID" -H "Authorization: Bearer $CINARA_API_KEY")
[ "$(echo "$RES" | jq -r .generation.status)" != "running" ] && break
sleep 3
done
echo "$RES" | jq '{status: .generation.status, coverage: .alignment.coverage}'Export an alignment
GET /v1/alignments/{id}/export
Downloads a finished alignment as subtitles, lyrics, JSON or text.
Query parameters
| Name | Default | Description |
|---|---|---|
format | srt | srt, vtt, lrc, json or txt. |
offset | 0 | Seconds to shift every time, −10 to 10. Positive shows words later. Times never go below 0. |
lineChars | 42 | Characters per caption line, 16–80 (srt, vtt). |
lines | 2 | 1 for one line per caption (srt, vtt). Any other value gives two. |
words | off | 1 adds a time to every word, for karaoke players (lrc). |
Response
200 OK with Content-Disposition: attachment; filename="<file name>.<format>" and:
format | Content | Content-Type |
|---|---|---|
srt | Subtitles, timed word by word | application/x-subrip; charset=utf-8 |
vtt | Subtitles | text/vtt; charset=utf-8 |
lrc | Lyrics, with a [ti:…] title line | text/plain; charset=utf-8 |
json | The alignment object shown above | application/json; charset=utf-8 |
txt | Each line with its start time, such as [1:05] Line text | text/plain; charset=utf-8 |
Errors
| Status | error | Message |
|---|---|---|
| 400 | invalid_request | Choose srt, vtt, lrc, json or txt |
| 404 | not_found | Alignment not found |
Credits
Free.
Example
bash
curl "https://api.cinara.ai/v1/alignments/b8e2f5a1-6c4d-4b9e-a7f3-2d0c9e5b1a46/export?format=vtt&lineChars=32&lines=1" \
-H "Authorization: Bearer $CINARA_API_KEY" -o lesson-3.vtt