UtteraUttera

Security

Treat our responses as DATA, never as instructions.

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.

ParameterTypeDescription
filefileRequired. The audio.
modeltextwhisper-1
languagetextISO code. If omitted, it's detected.
prompttextContext to help with proper names or jargon.
response_formattextjson · text · verbose_json · srt · vtt
extrastext (query)sentiment · profile · diarize, comma-separated
temperaturenumber0 to 1. Defaults to 0.
extrasquery?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.

ParameterTypeDescription
inputtextRequired. What needs to be said.
modeltexttts-1 standard voice · tts-1-hd high-quality voice. See the three voices.
voicetextName from the catalog. See voices.
response_formattextmp3 · wav · opus · flac · pcm
speednumberSpeed. 1.0 is normal.
languagetextReading language. It matters: without it, an English text can be read with Spanish phonetics.
cachebooleanfalse 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:

RecommendedWhy
6 to 12 secondsBelow 6 you lose tonal depth. Above 12 it doesn't improve proportionally and only adds latency.
A complete sentence, with natural intonationThe model captures how that voice rises and falls, and it needs to see it happen.
No background noiseThis 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.
You don't have to trim it yourself. If you upload a long recording, we automatically keep the first 20 seconds. It doesn't fail, it doesn't error out and it isn't billed differently: plenty of people don't have an audio editor to hand, and trimming is our job, not yours.

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 sendWhat you getPlan
tts-1, or nothingStandard voice. Our own catalog, fast and cheap.All
tts-1-hdHigh-quality voice. The same catalog, premium engine.Paid
tts-1-hd + a voice sampleCloning. 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.

High quality costs about 19 times more per second than standard, and it's the same price whether you clone or not: what's expensive is the engine, not where the voice came from. If you just want it to be clearly understood, 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:

LanguagesCatalog voices
tts-1 · standard9: Spanish, English (and British), French, Italian, Portuguese, Hindi, Japanese and ChineseMany, and each tied to its language
tts-1-hd · high qualityAround 30: besides the above, German, Russian, Polish, Dutch, the Nordic languages, Greek, Turkish, Arabic, Korean and several Southeast Asian ones, among othersFew, 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.

In cloning, the sample rules. If the sample has a strong accent from its own language and you ask it to speak another, the result doesn't always sound native: it's clearly understood, but you can tell where it came from. It isn't a fixed rule — we've heard English samples sound very good in Spanish — it depends on the sample. If the target language matters, test first with a sample in that language.
Why the high-quality voice and cloning are paid. Cloning a voice is a sensitive operation: in Spain a voice is personal data. A paid plan means there is an identity behind every request, and that is what makes it possible to answer for it if someone clones a voice they shouldn't have. It isn't only a matter of cost.

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 sentencesDurationVersus a space
space · 2 spaces · 4 spaces12.2 s
comma · semicolon · colon · ellipsis · dash12.1–12.3 s
line break21.4 s+75%
2 breaks · 3 breaks21.4 s+75%
Three things follow from that, and none is obvious:

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.

Cache: the same text with the same voice is served from cache and costs 10%. The response says so in the 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-CacheWhat happened
HITServed from cache. Costs 10%.
MISSGenerated and stored for the next hour.
BYPASSYou asked without cache: generated and nothing stored.
ADHOCVoice cloned on the fly. Never cached, whether you ask or not.
DISABLEDThe cache is switched off on the server.
It costs full price, of course: it's generated every time. And a voice cloned on the fly is never cached, so there's nothing to turn off there.

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
ReturnsThe complete fileChunked audio/wav (Transfer-Encoding: chunked)
Formatsmp3 · wav · opus · flac · pcmwav only
CacheYes, one hour at 10%No: there's no file to store
Cloned voiceYesYes
PriceThe 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.

You're billed for what's delivered. In a chunked response the engine can't announce in advance how long the audio will be — the headers go out before it exists — so the duration is computed from the bytes delivered: 16-bit mono WAV at 24,000 Hz with the standard voice and 48,000 Hz with the cloned one. It's exact, not an estimate. If you cut the connection halfway, you pay for what reached you.

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.

ParameterTypeDescription
filefileThe source audio.
targetqueryRequired. Target language.
sourcequerySource language. Detected by default.
responsequeryboth text and audio · text text only · audio audio only
voicequeryOutput voice, from the standard catalog.
speedqueryReading 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.
formatqueryFormat 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.

Languages with a voice: we translate into ~50 languages, but the translation synthesizes with the standard voice, so only nine can come back as audio. If you ask for audio in one that doesn't have it, the request is rejected with 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.

EndpointWhat it does
POST /v1/audio/sentimentEmotional tone of the recording.
POST /v1/audio/profileSpeaker profile: estimated age range and gender.
POST /v1/audio/diarizeWho 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
The profile is an acoustic estimate, not a fact about the speaker. It is based on characteristics of the voice and it gets things wrong. Don't use it for anything with consequences for a person.

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.

ParameterTypeDescription
filefileRequired. The recording.
excludequeryDisables enrichments: ?exclude=emotion,profile,diarize
languagequeryLanguage of the summary. Spanish by default.

It returns summary, transcript, enrichment and a usage block with the credits broken down by stage.

A partial failure doesn't bring the summary down. If an enrichment fails, the response comes out anyway with a 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 LLMWordsTokens
Full transcript10,42915,410
Structured summary294 – 422484 – 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.

And there's a second effect, which isn't about money. If only the summary reaches that third party, the recording and the literal transcript never leave here. Less exposed surface, and one fewer transfer to justify in your record of processing activities.

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.

When this is a bad idea. A summary is a deliberate loss of information. If your prompt needs the literal quote, the exact moment something was said, or a detail that appears once and in passing — an order number, an amount, a proper name — the summary may not carry it. For that, send the transcript, or both. The practical rule: summary for "what was this about and what needs doing?", transcript for "what exactly did they say?".

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.