Text-to-SpeechNew
Discover voices, estimate cost, generate speech, and manage durable audio clips.
Addis Voices 2 turns Amharic and Afaan Oromo text into durable audio clips. It is billed by generated duration at 5 ETB per minute and does not stream partial audio.
Maintaining a Voice 1 integration?
The original Text-to-Speech API is deprecated and hidden from the primary navigation. Open the Legacy Text-to-Speech guide →
Discover voices
Query the catalog rather than copying an arbitrary ID into production configuration. am-hamen is the canonical Amharic example.
const voices = await addis.voices.list({ language: "am" });
for (const voice of voices) {
console.log(voice.id, voice.name, voice.isAvailable);
}voices = addis.voices.list(language="am")
for voice in voices:
print(voice["id"], voice["name"], voice["is_available"])curl "https://api.addisassistant.com/api/v1/voice/voices?language=am" \
-H "x-api-key: $ADDIS_API_KEY"The catalog is dynamic. Filter for the requested language and store only IDs currently marked available. Each entry includes the voice ID, name, descriptor, language, gender, style, tags, preview URL, default status, and availability.
Catalog filters
| Filter | Node.js | Python | Values |
|---|---|---|---|
| Language | language | language | am or om |
| Gender | gender | gender | female or male |
| Search | search | search | Name, ID, descriptor, style, or tag text |
| Include unavailable voices | includeUnavailable | include_unavailable | false by default |
Browse current voice IDs
The playable catalog below is synchronized with the current backend availability list and developer-platform previews, and was verified on July 23, 2026. Your application should still call voices.list() because availability can change without a documentation release.
Current playable voices
Preview the current catalog and copy the exact ID into your request. Live availability still comes from voices.list().
Showing 6 of 19 Amharic voices
- female
Hamen
Warm conversational delivery
am-hamenConversational - male
Yohannes
Low-key reflective tone
am-yohanes-calmNarration - male
Tesfa
Confident, forward-leaning read
am-tesfaCommercial - male
Muaz
Clear expressive delivery
am-muazCommercial - male
Roba
Energetic bright tone
am-robaCommercial - female
Nejat
Smooth measured elegance
am-nejatConversational
Preview a voice
const preview = await addis.voices.preview("am-hamen");
console.log(preview.audioUrl);preview = addis.voices.preview("am-hamen")
print(preview["audio_url"])curl https://api.addisassistant.com/api/v1/voice/voices/am-hamen/preview \
-H "x-api-key: $ADDIS_API_KEY"Preview URLs are signed and can expire. Fetch fresh preview metadata instead of storing the URL permanently.
Estimate cost
Estimate before generation when a user must approve spend or the application is close to its available balance.
const input = {
text: "ሰላም፣ ይህ የአዲስ ድምፅ ሁለት ሙከራ ነው።",
voiceId: "am-hamen",
language: "am" as const,
outputFormat: "mp3_44100" as const,
};
const estimate = await addis.voice.estimate(input);
if (!estimate.canGenerate) throw new Error("Insufficient balance");
console.log(estimate.estimatedCost, estimate.currency);input = {
"text": "ሰላም፣ ይህ የአዲስ ድምፅ ሁለት ሙከራ ነው።",
"voice_id": "am-hamen",
"language": "am",
"output_format": "mp3_44100",
}
estimate = addis.voice.estimate(**input)
if not estimate["can_generate"]:
raise RuntimeError("Insufficient balance")
print(estimate["estimated_cost"], estimate["currency"])curl https://api.addisassistant.com/api/v1/voice/estimate \
-H "x-api-key: $ADDIS_API_KEY" \
-H "content-type: application/json" \
-d '{
"text":"ሰላም፣ ይህ የአዲስ ድምፅ ሁለት ሙከራ ነው።",
"voice_id":"am-hamen",
"language":"am",
"output_format":"mp3_44100"
}'The estimate reports the pricing unit, price per minute, estimated duration, billable duration, cost, current balance, projected balance, currency, and whether generation is currently allowed.
Generate and save a clip
const clip = await addis.voice.generate({
...input,
clientRequestId: crypto.randomUUID(),
});
await clip.toFile("speech.mp3");
console.log(clip.id, clip.audioUrl, clip.usage);clip = addis.voice.generate(
**input,
client_request_id="order-123",
)
clip.to_file("speech.mp3")
print(clip.id, clip.audio_url, clip.usage)curl https://api.addisassistant.com/api/v1/voice/generations \
-H "x-api-key: $ADDIS_API_KEY" \
-H "content-type: application/json" \
-d '{
"text":"ሰላም፣ ይህ የአዲስ ድምፅ ሁለት ሙከራ ነው።",
"voice_id":"am-hamen",
"language":"am",
"output_format":"mp3_44100",
"client_request_id":"order-123"
}' | tee voice.json
curl --location "$(jq -r '.data.audio_url' voice.json)" \
--output speech.mp3The cURL example requires jq. A generation response contains clip metadata and a signed audio_url. A first response may also include an audio data URL; an idempotent replay can omit that inline field, so use the signed URL or SDK file helper.
Output formats
| Format | Use |
|---|---|
mp3_44100 | Compact delivery for web, mobile, and downloads. |
wav_44100 | Uncompressed editing and audio pipelines. |
pcm_16000 | Speech pipelines expecting 16 kHz PCM in a WAV container. |
Choose the format before estimating because output choice can affect generated duration and storage behavior.
Idempotency and retries
clientRequestId in Node.js and client_request_id in Python identifies one logical generation.
- Reuse it when retrying the same request after a timeout or uncertain response.
- Generate a new value for different text, voice, language, or output format.
- Do not retry a validation error unchanged.
- For a generation-in-progress response, wait for the reported retry interval before checking again.
Voice settings
speed is currently applied. stability, similarity, and style are accepted for compatibility but ignored by the current provider. The response reports ignored settings in meta.ignoredVoiceSettings or meta["ignored_voice_settings"].
Do not build user-facing behavior that assumes an ignored control changes the generated clip.
Usage and clip history
const usage = await addis.voice.usage();
const page = await addis.voice.clips.list({ limit: 20 });
const clip = await addis.voice.clips.get(page.data[0].id);
await clip.toFile("saved-clip.mp3");
await addis.voice.clips.delete(clip.id);usage = addis.voice.usage()
page = addis.voice.clips.list(limit=20)
clip = addis.voice.clips.get(page.data[0]["id"])
clip.to_file("saved-clip.mp3")
addis.voice.clips.delete(clip.id)curl https://api.addisassistant.com/api/v1/voice/usage \
-H "x-api-key: $ADDIS_API_KEY"
curl https://api.addisassistant.com/api/v1/voice/clips \
-H "x-api-key: $ADDIS_API_KEY"
curl -X DELETE https://api.addisassistant.com/api/v1/voice/clips/CLIP_ID \
-H "x-api-key: $ADDIS_API_KEY"Addis Voices 2 usage is billed at 5 ETB per generated minute. Use the estimate before generation when a user must approve spend, and use the usage response to display current ETB balance and total spend.
Errors
Common failures include unavailable voice IDs, unsupported formats, insufficient balance, idempotency conflicts, generation still in progress, rate limiting, and transient provider errors.
Refresh the voice catalog after an unavailable-voice error. Log the clip ID, request ID, and client request ID, but never the API key or sensitive full text.
Best Practices
Ensure high-quality output and reliable billing with these production patterns.
Script & Punctuation
Voice Selection
Cost & Retries
Clip Delivery
Migrate from Legacy Text-to-Speech
Addis Voices 2 uses the same Addis AI API key as the legacy API, so no credential migration is required. HTTP header names are case-insensitive: the SDK reads ADDIS_API_KEY, and raw requests send it through x-api-key.
| Area | Legacy Text-to-Speech | Addis Voices 2 |
|---|---|---|
| Endpoint | /api/v1/audio | /api/v1/voice/generations |
| Authentication | Existing Addis AI API key in X-API-Key | The same API key through the SDK or x-api-key; no credential change |
| Request | text, language, and optional stream | text, voice_id, language, output_format, and optional client_request_id |
| Voice selection | Language selects the legacy engine | Query the voice catalog, preview an available voice, then send its exact ID |
| Response | Base64 WAV JSON or a legacy audio stream | Durable clip metadata and a signed audio_url; partial streaming is not supported |
| SDK | legacy.audio.generate() or legacy.audio.stream() | voice.generate(), voices.list(), estimates, usage, and clip management |
| Pricing | Existing Voice 1 account pricing remains unchanged | 5 ETB per generated minute, with a pre-generation estimate |
For a new integration, follow Generate and save a clip. Existing applications can keep using the complete Legacy Text-to-Speech guide while migrating one use case at a time. Review Pricing before switching production traffic.
Contract last verified: