Build with Audexum.
REST API. Bearer auth. JSON in, WAV/MP3/OGG/Opus out. Streaming and webhooks included.
Generate an API key from your dashboard. Send it as a Bearer token on every request.
Authorization: Bearer sk_live_•••••••••••••••••••••••••••••••••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.
curl -X POST https://audexum.com/api/synthesize \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"text": "Hello world",
"voice": "M1",
"lang": "en",
"speed": 1.05,
"steps": 8
}' \
--output hello.wavRequest body
Response headers
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) |
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.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 -OConfigure 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": 30000,
"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"| 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 | Rate-limited (>50 req/min on the same key) |
| 503 | Server busy — retry with exponential backoff |
- Global inflight cap of 50 concurrent synthesis requests
- Per-key soft limit of 50 requests/minute
- Per-month character quota from your plan tier
- Contact us for dedicated capacity or PAYG
Stuck? Email [email protected].
Get an API key