Build with Audexum.
REST API. Bearer auth. JSON in, WAV/MP3/OGG/Opus out. Streaming and webhooks included.
Authentication
Generate an API key from Developer settings. Send it as a Bearer token on every request.
Authorization: Bearer sk_live_•••••••••••••••••••••••••••••••••POST/api/synthesize
Generate audio from text. Returns audio/wav by default (44.1 kHz, 16-bit PCM, mono). Use format for MP3/OGG/Opus, or stream=true for low-latency chunked streaming.
# The shortest call there is. No backend named, so you get
# V3 Expressive on the Atlas voice — our most expressive engine.
curl -X POST https://audexum.com/api/synthesize \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"text": "Hello world"}' \
--output hello.wav
# V2 Multilingual, where speed and steps apply and 32 languages are available.
curl -X POST https://audexum.com/api/synthesize \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"text": "Hello world",
"backend": "v2",
"voice": "M1",
"lang": "en",
"speed": 1.05,
"steps": 8
}' \
--output hello.wavRequest body
Voice tiers
Pick a tier with backend. Each tier has its own voices and a per-character credit multiplier. Sending an unknown voice for a tier returns 400.
Omit backend and the tier is inferred: a V2 voice (F1..F5, M1..M5) keeps you on V2 Multilingual, a V3 voice selects V3 Expressive, and naming neither gives you V3 Expressive onAtlas. Existing integrations that send only avoice are unaffected, and keep billing at their tier's multiplier.
| backend | tier | cost | voices |
|---|---|---|---|
| v1 | V1 Instant: fast, English-first | 1× | See /api/voices |
| v2 | V2 Multilingual: 32 languages, incl. Bulgarian | 1.5× | F1..F5, M1..M5 |
| v3 | V3 Expressive: most expressive, emotion & style | 2× | Atlas, Reid, Sage, Kai, Cole, Ivy, Mira, Elle, Nova |
# V3 Expressive — most expressive tier, with an optional style prompt
curl -X POST https://audexum.com/api/synthesize \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"text": "We shipped it. Finally.",
"backend": "v3",
"voice": "Atlas",
"instruct": "warm, relieved, unhurried"
}' \
--output ultra.wavResponse headers
Output formats (OGG / Opus / MP3)
Add "format": "ogg" (or mp3 / opus) to get a transcoded response. Quota is identical regardless of format. The AudioSeal watermark survives MP3 encoding.
# OGG/Opus — smallest file size, best for streaming/web
curl -X POST https://audexum.com/api/synthesize \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"text": "Hello", "voice": "M1", "lang": "en", "format": "ogg"}' \
--output hello.ogg
# MP3 — maximum compatibility
curl -X POST https://audexum.com/api/synthesize \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"text": "Hello", "voice": "M1", "lang": "en", "format": "mp3"}' \
--output hello.mp3import requests
r = requests.post(
"https://audexum.com/api/synthesize",
headers={"Authorization": "Bearer sk_live_..."},
json={"text": "Hello world", "voice": "M1", "lang": "en", "format": "ogg"},
)
r.raise_for_status()
with open("hello.ogg", "wb") as f:
f.write(r.content)
# Content-Type: audio/ogg| format | Content-Type | Codec |
|---|---|---|
| wav | audio/wav | PCM 16-bit, 44.1 kHz (no transcode) |
| mp3 | audio/mpeg | libmp3lame VBR ~192 kbps |
| ogg | audio/ogg | libopus 96 kbps, 48 kHz |
| opus | audio/opus | libopus 96 kbps, 48 kHz (Ogg container) |
Streaming TTS
Add "stream": true to receive audio as it generates. The response is a chunked-transfer stream of concatenated WAV files. Each chunk is a complete, valid WAV (44-byte RIFF header + PCM) so you can begin playback immediately. Quota is reserved for the full text up front, identical to a non-streaming call. Streaming requires format=wav (the default).
curl -X POST https://audexum.com/api/synthesize \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"text": "Sentence one. Sentence two. Sentence three.", "voice": "M1", "lang": "en", "stream": true}' \
--no-buffer --output - | ffplay -nodisp -i pipe:0import requests
with requests.post(
"https://audexum.com/api/synthesize",
headers={"Authorization": "Bearer sk_live_..."},
json={
"text": "First sentence. Second sentence. Third one.",
"voice": "M1",
"lang": "en",
"stream": True,
},
stream=True,
) as r:
r.raise_for_status()
with open("streamed.wav", "wb") as f:
for chunk in r.iter_content(chunk_size=None):
f.write(chunk)
# Each yielded chunk is a standalone WAV file (header + PCM).
# Concatenating them produces valid gapless audio.wav. First-chunk latency is logged server-side. The response header X-Stream: 1 confirms streaming mode.POST/api/transcribe
Transcribe speech to text. Send audio as multipart/form-data. Requires an unrestricted key (widget keys are limited to /api/synthesize). Billed at 200 credits per minute of audio.
curl -X POST https://audexum.com/api/transcribe \
-H "Authorization: Bearer sk_live_..." \
-F "[email protected]" \
-F "timestamps=true"Form fields
| Field | Notes |
|---|---|
| audio | Required. An audio/* file, up to 25 MB and 600 seconds. |
| timestamps | Optional bool. Include per-segment start/end times. |
| vocabulary | Optional. Up to 2000 chars of domain terms to bias recognition. |
| clean / mode | Optional. Post-process the transcript (clean, email, professional, bullets, concise). |
Response
{
"text": "Full transcript…",
"duration_s": 12.3,
"transcribe_ms": 480,
"segments": [{ "start": 0.0, "end": 3.2, "text": "…" }],
"raw_text": "…", // present only when a clean/transform mode was requested
"mode": "clean"
}POST/v1/audio/transcriptions
OpenAI-compatible speech-to-text. A tool that lets you change its OpenAI base URL can use Audexum by setting it to https://audexum.com/v1, no code change. It runs the same engine and billing as /api/transcribe: 200 credits per minute of audio. Send multipart/form-data.
Auth is Authorization: Bearer sk_live_…. Create the key in Developer settings (sign-in required). Use a secret sk_live_ key; restricted widget keys get 403 here.
curl https://audexum.com/v1/audio/transcriptions \
-H "Authorization: Bearer sk_live_..." \
-F [email protected] \
-F model=whisper-1 \
-F language=bg \
-F response_format=json
# {"text": "…"}from openai import OpenAI
client = OpenAI(api_key="sk_live_...", base_url="https://audexum.com/v1")
with open("recording.mp3", "rb") as f:
result = client.audio.transcriptions.create(
model="whisper-1",
file=f,
language="bg", # optional; omit to auto-detect
)
print(result.text)Form fields
verbose_json
Segment start/end come from the engine. Word timings (timestamp_granularities[]=word) are estimated: each segment's span is divided across its words by character length. They are not forced-aligned. Every segment carries a words key, empty unless you asked for words.
{
"task": "transcribe",
"language": "bg", // echo of the request; "auto" when omitted
"duration": 12.3,
"text": "Full transcript…",
"segments": [
{ "id": 0, "start": 0.0, "end": 3.2, "text": "…", "words": [] }
]
}Not supported
POST /v1/audio/translationsreturns 405.- Streaming transcription and the Realtime API.
Errors
| Code | Body |
|---|---|
| 400 | {"detail": "audio_too_short"} · "no_speech_detected" · "clip_too_long: max 600s per request" |
| 401 | {"detail": "authentication_required"}: missing or invalid key |
| 402 | {"detail": {"error": "insufficient_credits", …}}: full body below |
| 403 | {"detail": "email_not_verified"}: verify the account's email address |
| 405 | {"detail": "Method Not Allowed"}: /v1/audio/translations |
| 413 | {"detail": "audio_too_large"}: file over 25 MB |
| 415 | {"detail": "unsupported_media_type"}: file part is not audio |
// 402 — the balance cannot cover this file
{
"detail": {
"error": "insufficient_credits",
"plan_tier": "free",
"monthly_grant": 250,
"monthly_used": 250,
"pack_credits": 0,
"requested": 41,
"upgrade_url": "/pricing",
"offer": {
"reason": "insufficient_credits",
"headline": "You are out of credits",
"detail": "This request needs 41 credits. …",
"plan": { "tier": "pro", "price_eur": 29, "credits_per_month": 1200000 },
"actions": [
{ "kind": "upgrade", "label": "Upgrade to Pro — €29/mo", "url": "https://audexum.com/r/402" },
{ "kind": "pack", "label": "Buy 250,000 credits — €7", "url": "https://audexum.com/pricing#packs" }
]
}
}
}POST/api/batch
Submit up to 50 texts in one request. Poll for completion, then download a zip.
# submit
curl -X POST https://audexum.com/api/batch \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{"texts": ["one", "two"], "voice": "F2", "lang": "en"}'
# poll
curl -H "Authorization: Bearer sk_live_..." \
https://audexum.com/api/batch/$JOB_ID
# download once status=done
curl -H "Authorization: Bearer sk_live_..." \
https://audexum.com/api/batch/$JOB_ID/download -OWebhooks
Configure a webhook endpoint in your Developer settings to receive signed POST notifications. We send JSON with an X-Audexum-Signature header. Verify it before processing.
Payload shape
{
"event": "quota.warning_80",
"data": {
"monthly_grant": 3000,
"monthly_used": 24000,
"monthly_remaining": 6000,
"overage_enabled": false,
"plan_tier": "free",
"period_end": "2026-06-30"
},
"timestamp": "2026-06-12T14:22:00.000000+00:00"
}Events
Signature verification
Every delivery includes X-Audexum-Signature: sha256=<hex>, computed as HMAC-SHA256 over the raw request body using your webhook secret.
import hashlib, hmac
def verify_signature(body: bytes, secret: str, sig_header: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, sig_header)
# In your FastAPI/Flask/Django handler:
# body = await request.body()
# sig = request.headers["X-Audexum-Signature"]
# if not verify_signature(body, WEBHOOK_SECRET, sig):
# return 401# Configure endpoint (session auth — browser only)
curl -X PUT https://audexum.com/api/account/webhook \
-H "Cookie: audexum_session=..." \
-H "X-Requested-With: XMLHttpRequest" \
-H "Content-Type: application/json" \
-d '{"url": "https://your-server.com/webhooks/audexum"}'
# Response contains secret — store it, shown once only.
# Remove endpoint
curl -X DELETE https://audexum.com/api/account/webhook \
-H "Cookie: audexum_session=..." \
-H "X-Requested-With: XMLHttpRequest"Errors
| Code | Meaning |
|---|---|
| 400 | Bad request: invalid voice/lang, empty text, or stream+non-wav format |
| 401 | Missing or invalid API key |
| 402 | insufficient_credits: Monthly quota exceeded; see /api/usage and /pricing for upgrade options |
| 402 | feature_gated: Feature requires a higher plan (see error.feature and error.min_tier) |
| 422 | Invalid webhook URL (not HTTPS, private IP, or unresolvable) |
| 429 | Too many requests: reduce your request rate and retry |
| 503 | Server busy (concurrency cap reached): retry with exponential backoff; honor Retry-After |
Rate limits & fair use
- Shared concurrency cap: requests beyond it get 503 + Retry-After
- Sustained abusive request rates may be throttled (fair use)
- Per-month credit quota from your plan tier
- Contact us for dedicated capacity or PAYG
Stuck? Email [email protected].