Text to speech
Synthesize speech from text with a preset or cloned voice.
Synthesize speech from text with a preset or cloned voice.
Primary agent path
Use the Universal Skill with the MCP tool beatra.speech.synthesize. The tool
is billable and returns an asynchronous task.
- Pick a voice first:
beatra.voices.listreturns preset and cloned voices withvoice_idand preview URLs. - If a model, language, format, or price matters, call
beatra.models.listwithcapability: "text_to_speech". Otherwise usemodel: "auto"or omitmodel. - Create one opaque
client_request_idand submit the final arguments once. - Poll the returned task with
beatra.tasks.getuntil it is terminal.
AI guide
MCP integration guide for AI
For custom API integrations, copy this guide into Claude, Codex, or another coding assistant.
Tool arguments
voice accepts any voice_id from voices,
preset or cloned. A voice belongs to
one engine (its compatible_models), and on some models what a voice follows
differs from voice to voice; its persona says so. Streaming and real-time
conversation are not part of this capability.
Speech markup
input may contain a closed set of tokens. Tokens count toward the 50,000
character limit and toward weighted-character billing. The live vocabulary and
ranges are constraints.script_markup on the model card from
beatra.models.list. The same markup works on every speech model; what a
model cannot render is degraded (dropped or ignored) with a warning, never
approximated. The model-specific options further down this page are
model_options fields, not markup.
{pause <seconds>}— silence, at most two decimal places, within the model card'sscript_markup.pause.range(up to 99.99 seconds on some models, 10 seconds on others; a longer pause is rejected before billing). The request is rejected when a pause sits at the start or end of the text, or when two pauses have no speakable content between them.- Sound tags:
{laugh}a laugh,{chuckle}a chuckle,{sigh}a sigh,{breath}an audible breath,{cough}a cough,{clear-throat}a throat clear,{gasp}a gasp,{groan}a groan,{inhale}an inhale,{exhale}an exhale,{pant}panting,{sniff}a sniff,{hmm}a hesitant "hmm".script_markup.tagslists the tags a model renders; on a model without them the tag is removed (never read aloud) and amarkup_degradedwarning says so. {pron <word> <reading>}— pin the reading of this one occurrence of a word; the same word elsewhere keeps its default reading, and one request may give the same character different readings. Readings are pinyin: syllables written together, each ending with a tone digit 1–5,vfor ü, one syllable per Chinese character. Other notations are not supported. Example:孩子{pron 长 zhang3}大了,头发也很{pron 长 chang2}。{sub <word> <spoken text>}— speak this occurrence of a word as the given text, up to 100 characters; subtitles still show the word. Use it for brand names, abbreviations, English words, and digits that must be read one by one. Example:{sub W3C 万维网联盟}.{{— a literal left brace: a{{in the text is spoken as one{, and the pair itself is never parsed as a token. Text after it is still parsed normally, so escape only the left brace and write right braces as they are.
constraints.limitations on each model's card states that model's limits in
plain sentences; read it for the model you call. Examples:
- Volume, speed, pitch, and emotion apply to the whole request and cannot target one sentence or word. For local variation, split into multiple calls (each with its own parameters and its own audio file) or rewrite the text (ellipses, short clauses, wording).
- On some models, long texts (over 3,000 characters, which take the async
path) do not get high-quality number normalization, or drop
{pause}and{pron}markup (with a warning). Split long texts at paragraph breaks into requests of at most 3,000 characters when that matters.
Performance direction and previous text
Two optional fields describe how a clip should sound, on every model that
follows them; the model card lists each one under controls (with
constraints.direction_max_characters and
constraints.previous_text_tail_characters):
direction— natural-language direction for the whole request, for example像在耳边说悄悄话一样,轻声地说orread it like breaking news. Keep it to a sentence or two: a direction longer thandirection_max_charactersis ignored with a warning. How it comes through depends on the model, the voice and the text; a volume request is better made withvolume.previous_text— the text that comes right before this clip (the previous line of a dialogue, the previous paragraph), so separately synthesized clips keep a continuous tone. Any length is accepted; the model uses only its lastprevious_text_tail_characterscharacters. It is never spoken.
Neither field is billed, and neither ever makes a request fail. A model, a
voice, or a path that does not follow a field ignores it and returns an
option_ignored warning; the audio is still produced. On some models these
controls depend on the voice (for example, character voices may follow
neither); constraints.limitations on the card says which. emotion works the same
way: a model or voice that cannot render the requested emotion ignores it
with a warning. To learn whether a voice follows these controls before a
billable call, send the same request to validate (below): it returns the same
warnings, free.
Before a billable submit, call beatra.speech.validate or
POST /v1/audio/speech/validate. It runs the same checks and warnings and
returns the weighted character count and the credit price; it does not create
a task, charge credits, or call the speech engine. It does not take an
Idempotency-Key.
Warnings, when present, are on the 202 response (including an idempotent
replay), GET /v1/tasks/{task_id}, beatra.tasks.get, and the customer
callback field task.warnings. The task list, expand, account detail, and
admin responses do not promise them. text_begin and text_end in a subtitle
file are offsets into the text the engine actually read — the text with
markup compiled into the engine's own form or removed — and are not mapped
back to the submitted text. Where a subtitle segment lines up with that text,
its text shows a {pron} or {sub} word as written and leaves sound tags
out. Billing counts characters as described under
Models, controls, and cost; output.characters
reports the characters the engine read, so the two counts can differ.
Models, controls, and cost
Common controls are input (required, ≤50,000 characters), voice
(required), language, format (default mp3; the card's
constraints.formats lists the others the model renders), speed (0.5–2.0),
volume (within the card's constraints.volume range, for example >0–10 or
0.5–2; a value outside it is rejected before billing), pitch (−1–1, normalized: 0 keeps the
voice's original pitch, −1 the lowest, 1 the highest the selected model
supports; use decimals like 0.25 for subtle tuning), emotion,
sample_rate, and subtitle_enable (default false; set true to receive
sentence-level subtitles with the audio as output.subtitles — free; a model
that cannot time subtitles for the chosen voice or language rejects the request
before billing, and constraints.limitations names those cases; if a subtitle
file cannot be produced at run time, the audio is still delivered, without
output.subtitles). language is a hint some models only partly use:
constraints.limitations says how the selected model treats it. Use each only according to the
selected model's metadata. Not every format supports every sample rate (for
example opus renders at 8/16/24 kHz on some models and only at its fixed
48 kHz on others, where it takes no sample_rate): read
constraints.sample_rates_by_format on the model card instead of assuming the
flat rate list applies to every format.
beatra.models.list is the current source for selectable models, supported
languages, formats, sample rates, and prices. Do not copy a model
catalog or price table into an agent prompt.
Speech is prepaid from the request estimate and billed linearly by
weighted characters (quoted per 1,000): each Han ideograph counts as 2 and
every other character counts as 1. The count is taken on the submitted text,
markup included. On a model whose engine bills the control text it receives by
length, the text sent to the engine is counted when it is longer: there,
pauses and readings become control tags, and a request that carries them (or
whose text begins with <speak) is sent whole in that form (&, < and >
escaped). Validate returns the exact count.
Model-specific options
model_options carries options that only one model family runs, named by
family. The model card lists the keys it runs under controls (for example
model_options.doubao.explicit_dialect). Keys for another family have no
effect; a model may say so with an option_ignored warning.
MiniMax: pronunciation dictionary
For rare or polyphonic Chinese characters, pin the exact reading instead of rewriting the text with a homophone:
The format is character/(pinyin+tone digit), with tone digits 1–4 for the
four tones and 5 for the neutral tone. A dictionary entry applies to every
occurrence of the word in the request; to fix one occurrence, use the
{pron} markup above. MiniMax models only; other models ignore this field.
Doubao
model_options.doubao.explicit_dialect—beijing,dongbei,henan,shaanxi,shanghai,sichuanortianjin(Cantonese islanguage: "yue-HK"). Only voices whoselanguages_supportedlistsyuespeak dialects; other voices are rejected before billing.model_options.doubao.explicit_language— forces the reading language (zh-cn,en,ja,es-mx,ko,de,fr, …). This is a filter, not a hint: sentences in other languages are skipped, and a voice given text it cannot read produces no audio (the task fails and is refunded).nl,no,uk,fi,da,cs,hu,el,ro,hiandes-eswork only with Chinese (zh_…) preset voices. Long texts accept onlyzh-cn,en,es-mx,idandpt-br.model_options.doubao.silence_duration— silence appended after the last word, 0–30,000 milliseconds.model_options.doubao.latex_parser—"v2"reads LaTeX formulas aloud (education scripts).model_options.doubao.pronunciation_dict—{"tone": ["北京/(bei3)(jing1)", "omg/oh my god"]}: up to 5,000 rules applied to every occurrence of the word (a reading, or a replacement text). To fix one occurrence, use{pron}.
When the text uses {pause} or {pron}, the markup wins: latex_parser and
pronunciation_dict are ignored with a warning, because the engine cannot
combine them with the form that markup compiles to.
Languages
language is an optional provider-neutral BCP-47 tag such as zh-CN,
yue-HK, or en-US. Beatra matches the primary language subtag against the
selected model; omit the field to let the voice or provider choose. Pass the
exact value auto only when you explicitly want automatic language detection
(constraints.language_auto on the model card says whether the model supports
it). An unsupported language is rejected instead of being silently replaced.
How a model uses language differs. Some use it as a recognition hint for
the voice. Others read Chinese and English by default and use language to
turn on language recognition for anything else (a Chinese voice may read
nothing in, say, Japanese without it) and to pick a dialect (yue); on those,
each voice reads only the languages in its languages_supported. A
language the voice does not speak comes back with an option_ignored
warning, except where the result could only fail: Cantonese on a voice
without it, or a long text in a language the voice cannot read, is rejected
before billing. constraints.limitations on the card says which applies.
Current speech models cover up to the 40 languages below, each model a part
of them; constraints.supported_languages from beatra.models.list is the
live, model-specific source.
| Code | Language | Code | Language |
|---|---|---|---|
zh | Chinese | tr | Turkish |
yue | Cantonese | nl | Dutch |
en | English | uk | Ukrainian |
es | Spanish | th | Thai |
fr | French | pl | Polish |
ru | Russian | ro | Romanian |
de | German | el | Greek |
pt | Portuguese | cs | Czech |
ar | Arabic | fi | Finnish |
it | Italian | hi | Hindi |
ja | Japanese | bg | Bulgarian |
ko | Korean | da | Danish |
id | Indonesian | he | Hebrew |
vi | Vietnamese | ms | Malay |
fa | Persian | sk | Slovak |
sv | Swedish | hr | Croatian |
fil | Filipino | hu | Hungarian |
no | Norwegian | sl | Slovenian |
ca | Catalan | nn | Nynorsk |
ta | Tamil | af | Afrikaans |
Task status and recovery
queued and running are not failures. Keep polling the same task_id; never
submit a replacement because work is still running. On a lost create response,
retry the identical arguments with the same client_request_id. If any input
changes, use a new ID. Failed tasks automatically refund the charged credits.
Return the audio artifact, resolved model, and charged credits only from the
terminal task response; artifact URLs are CDN addresses — copy them to your own
storage promptly.
REST API
Direct protocol integrations may use POST /v1/audio/speech and the shared
task endpoints. Follow the generated API operation.
Skill + MCP is the recommended integration; use REST for custom, non-agent
applications.
Last updated