Security
Transcripts, translations and summaries are generated from audio we do not control: your customer produces it or it comes from them. Anyone can try to slip sentences shaped like commands into a recording so that they get executed in your system.
We harden the summary against that kind of manipulation, but no defense is complete. If you're going to pass our output to an agent, a CRM or any automation with permissions, treat it as untrusted text: don't execute it, don't read it as orders, and validate it before acting on it.
And what a speaker claims in a recording is not a proven fact, even if it ends up in the summary.
Transcribe
POST /v1/audio/transcriptions
Turns a recording into text. Detects the language on its own.
You can use this service directly, without writing code, on the Studio page: the Transcribe audio card.
| Parameter | Type | Description |
|---|---|---|
file | file | Required. The audio. |
model | text | whisper-1 |
language | text | ISO code. If omitted, it's detected. |
prompt | text | Context to help with proper names or jargon. |
response_format | text | json · text · verbose_json · srt · vtt |
extras | text (query) | sentiment · profile · diarize, comma-separated |
temperature | number | 0 to 1. Defaults to 0. |
extras | query | ?extras=sentiment adds tone analysis in the same request. |
The response carries the X-Audio-Duration header with the exact seconds that
were billed.
Text to speech
POST /v1/audio/speech
You can use this service directly, without writing code, on the
Studio page: the Text to digital voice
(tts-1) and Text to cloned voice (tts-1-hd) cards.
| Parameter | Type | Description |
|---|---|---|
input | text | Required. What needs to be said. |
model | text | tts-1 standard voice · tts-1-hd high-quality voice. See the three voices. |
voice | text | Name from the catalog. See voices. |
response_format | text | mp3 · wav · opus · flac · pcm |
speed | number | Speed. 1.0 is normal. |
language | text | Reading language. It matters: without it, an English text can be read with Spanish phonetics. |
cache | boolean | false keeps this request out of the cache: nothing is read and nothing is written. See below. |
curl -X POST https://api.uttera.ai/v1/audio/speech \
-H "Authorization: Bearer $UTTERA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"tts-1","input":"Your order ships tomorrow.","voice":"nova","response_format":"mp3"}' \
--output voice.mp3
The voice sample: what to upload
Cloning a voice needs a reference sample. What works best:
| Recommended | Why |
|---|---|
| 6 to 12 seconds | Below 6 you lose tonal depth. Above 12 it doesn't improve proportionally and only adds latency. |
| A complete sentence, with natural intonation | The model captures how that voice rises and falls, and it needs to see it happen. |
| No background noise | This is what most people get wrong. Music, room echo or air conditioning contaminate the output voice and will show up in everything you generate afterwards. |
On whose voice you may clone — which is the question that causes the most trouble — see the legal framework: a voice is protected, and public material is not an authorization.
Three voices, not two
The same route gives three different things, and what decides which is what you send:
| What you send | What you get | Plan |
|---|---|---|
tts-1, or nothing | Standard voice. Our own catalog, fast and cheap. | All |
tts-1-hd | High-quality voice. The same catalog, premium engine. | Paid |
tts-1-hd + a voice sample | Cloning. Your voice, on the premium engine. | Paid |
The sample is sent as custom_voice_file in a multipart form; without
it, the request goes as JSON. That's the whole difference between asking for a catalog voice
in high quality and cloning one.
tts-1 is the
sensible option.They don't speak the same languages
It's the difference that surprises people most, and it isn't in the timbre:
| Languages | Catalog voices | |
|---|---|---|
tts-1 · standard | 9: Spanish, English (and British), French, Italian, Portuguese, Hindi, Japanese and Chinese | Many, and each tied to its language |
tts-1-hd · high quality | Around 30: besides the above, German, Russian, Polish, Dutch, the Nordic languages, Greek, Turkish, Arabic, Korean and several Southeast Asian ones, among others | Few, but each speaks all 30 |
The reason is that they are engines of a different nature. The standard one has a fixed catalog of voices, each trained for its language. The high-quality one starts from a sample — which is why it can clone — and that same voice reads any of the languages it knows.
With tts-1-hd there's no need to declare the language: it's inferred
from the text. With tts-1 it does matter, because the chosen voice is what
fixes it.
Line breaks cost money
Speech is billed per second generated, not per character. And a line break makes the engine insert a pause. So the same text costs differently depending on how it's formatted, and it's worth knowing before the invoice surprises you.
Measured with the same sentence repeated eight times, changing only what's in between:
| Between sentences | Duration | Versus a space |
|---|---|---|
| space · 2 spaces · 4 spaces | 12.2 s | — |
| comma · semicolon · colon · ellipsis · dash | 12.1–12.3 s | — |
| line break | 21.4 s | +75% |
| 2 breaks · 3 breaks | 21.4 s | +75% |
Spaces do nothing. Neither two nor four. Nor does punctuation: a comma, a period or a dash sound the same as a space as far as the clock is concerned.
Only the line break creates a pause, and each one costs 1.31 s (0.028 credits).
More breaks don't lengthen the pause. One, two or three are exactly the same. They are no use for asking for a longer wait.
On a real text the difference isn't small. La canción del pirata, with its ~96 lines, spends 126 seconds on pauses alone: a little over half of what synthesizing the whole thing costs.
If you don't want the pauses, strip the line breaks before sending the text: the same words as one running paragraph cost nearly half. And if you do want them, now you know what they're worth.
Why the chain gives a different number
If you transcribe an audio file and then translate it, you'll see that the speech for the translation costs considerably less than the original synthesis of the same text. It isn't a billing error: recognition returns one running paragraph, without the original's line breaks. That flat text is synthesized without pauses, and so it lasts — and costs — less.
Put another way: line breaks don't survive the trip through audio. If keeping them matters to you, hold on to the source text; they can't be recovered from the transcript.
X-Cache: HIT header.
The cache is per node. Since requests are spread across several, the first repetitions of a text may not hit: each node fills it the first time its turn comes. After that it hits.
Turning off the cache, request by request
Some work can't have the audio sitting on someone else's disk even for an hour: medical dictation, legal notes, a personal message. You can turn the cache off yourself, on each request, without asking us for anything and without changing your account. The audio is generated and delivered to you all the same; what doesn't happen is anything being written to or read from disk.
Three equivalent ways, whichever fits you best:
# 1) In the JSON body
-d '{"model":"tts-1","input":"Private notes","voice":"nova","cache":false}'
# 2) As a form field (accepts 0 / false / no / off)
-F input="Private notes" -F voice=nova -F cache=false
# 3) With the plain old HTTP header, without touching the body
-H "Cache-Control: no-cache"
The response always tells you what was done, so you don't have to take our word for it:
X-Cache | What happened |
|---|---|
HIT | Served from cache. Costs 10%. |
MISS | Generated and stored for the next hour. |
BYPASS | You asked without cache: generated and nothing stored. |
ADHOC | Voice cloned on the fly. Never cached, whether you ask or not. |
DISABLED | The cache is switched off on the server. |
Streaming: hearing it as it's generated
POST /v1/audio/speech/stream
The normal speech endpoint gives you the file when it's complete. This one hands it to you as it's generated, in chunks, so the first sound comes out almost immediately instead of waiting for the last word.
If you're building a phone system, an assistant that answers, or anything where a person is waiting on the other end, this is the difference between a conversation and a queue.
/v1/audio/speech | /v1/audio/speech/stream | |
|---|---|---|
| Returns | The complete file | Chunked audio/wav (Transfer-Encoding: chunked) |
| Formats | mp3 · wav · opus · flac · pcm | wav only |
| Cache | Yes, one hour at 10% | No: there's no file to store |
| Cloned voice | Yes | Yes |
| Price | The same: per second of audio generated | |
curl -N -X POST https://api.uttera.ai/v1/audio/speech/stream \
-H "Authorization: Bearer $UTTERA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"tts-1","input":"Your order ships tomorrow.","voice":"nova","language":"en"}' \
--output - | aplay
curl's -N matters: without it, curl buffers the response and you lose
exactly what you came for.
Translate
POST /v1/translate
The full chain: it transcribes the audio, translates the text and synthesizes it in the target language. It accepts audio or text input, but not text to text: if text goes in, the output has to include audio.
You can use this service directly, without writing code, on the Studio page: the Translate a recording card.
| Parameter | Type | Description |
|---|---|---|
file | file | The source audio. |
target | query | Required. Target language. |
source | query | Source language. Detected by default. |
response | query | both text and audio · text text only · audio audio only |
voice | query | Output voice, from the standard catalog. |
speed | query | Reading speed of the returned audio. 1.0 is normal; 0.25 to 4.0. Since speech is billed per second generated, it also changes the price. |
format | query | Format of the returned audio: mp3 (default) · wav · opus · flac · pcm. It's synthesized with the standard voice, so pcm comes out at 24,000 Hz. |
curl -X POST "https://api.uttera.ai/v1/translate?target=en&response=both" \
-H "Authorization: Bearer $UTTERA_API_KEY" \
-F file=@recording.mp3
It returns source_text, the translated text, and
audio in base64 with its audio_format.
422 before spending anything and returns the valid list. Use
response=text for the rest.
It's a limitation of the engine this chain uses, not of the product: the high-quality voice speaks around 30 languages. If you need audio in one of the others, tell us.
Analyze the voice
Three endpoints over the same audio, each with its own price.
You can use these analyses directly, without writing code, on the Studio page: they are checkboxes on the Transcribe audio card, ticked over the same audio.
| Endpoint | What it does |
|---|---|
POST /v1/audio/sentiment | Emotional tone of the recording. |
POST /v1/audio/profile | Speaker profile: estimated age range and gender. |
POST /v1/audio/diarize | Who speaks and when, with timestamps per speaker. |
curl -X POST https://api.uttera.ai/v1/audio/diarize \
-H "Authorization: Bearer $UTTERA_API_KEY" \
-F file=@recording.mp3
Summarize a recording
POST /v1/summarize Professional and above
It transcribes, analyzes tone, profiles the speaker, separates speakers, and out of all that generates a structured summary. The four source services run in parallel, so the wait is that of the slowest, not the sum.
You can use this service directly, without writing code, on the Studio page: it's one more checkbox on the Transcribe audio card.
| Parameter | Type | Description |
|---|---|---|
file | file | Required. The recording. |
exclude | query | Disables enrichments: ?exclude=emotion,profile,diarize |
language | query | Language of the summary. Spanish by default. |
It returns summary, transcript, enrichment and a
usage block with the credits broken down by stage.
warnings array, and that stage
isn't billed.Saving tokens on your LLM
This is the use of the summary that fewest people see and that saves the most money. If what you want is for a language model from another provider — Claude, GPT, Gemini, whichever — to work on what was said in a call, the expensive route is sending it the whole transcript. The cheap one is sending it the summary.
Measured on a real 70-minute recording of varied prose, counting with the
o200k_base tokenizer:
| What you send the LLM | Words | Tokens |
|---|---|---|
| Full transcript | 10,429 | 15,410 |
| Structured summary | 294 – 422 | 484 – 673 |
It's given as a range because the summary is not deterministic: the same recording, run twice, gave 484 and 673 tokens. Between 20 and 30 times fewer input tokens, and the saving grows with duration: the transcript grows in a straight line with the minutes of recording, and the summary doesn't — it stays at a few hundred tokens. On a short recording it makes no difference; on an archive of calls it's the difference between an LLM invoice you can pay and one you can't.
The response brings summary and transcript in the same
call, so you don't have to choose in advance or pay twice: you decide in your code which
of the two goes up to the LLM.
There's a complete, runnable example — summarize here, send only the summary to the LLM and count the tokens you saved — in uttera-examples/llm-tokens.