صوتناSAUTNA

API reference

Sautna API

One endpoint. Send text with your API key, receive a WAV file (24 kHz, mono, 16-bit). Create keys in Studio → API keys.

Synthesize speech

POST /api/v1/tts

FieldTypeDescription
textstringThe Arabic text to speak. Per-request length depends on your plan.
voicestringVoice id. zol (male, default) or zola (female).
formatstringOutput encoding: wav (default), mp3, opus, ulaw_8000 (8 kHz μ-law for telephony) or pcm (headerless 24 kHz s16le). Ignored when stream is true.
streambooleantrue streams raw 24 kHz 16-bit mono PCM as sentences render — playback can start on the first sentence. Default returns a complete WAV.

Success responses include X-Characters-Billed, X-Quota-Remaining, X-RateLimit-Limit and X-RateLimit-Remaining headers; a 429 carries Retry-After (seconds). Errors are JSON: {"error": {"code", "message"}}. There's also an OpenAPI spec for Postman or client codegen.

curl

curl -X POST https://sautna.com/api/v1/tts \
  -H "Authorization: Bearer sautna_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"voice": "zol", "text": "أهلين! كيف اخبارك؟"}' \
  --output speech.wav

Python

import requests

resp = requests.post(
    "https://sautna.com/api/v1/tts",
    headers={"Authorization": "Bearer sautna_sk_..."},
    json={"voice": "zol", "text": "أهلين! كيف اخبارك؟"},
)
resp.raise_for_status()
open("speech.wav", "wb").write(resp.content)

JavaScript

const res = await fetch("https://sautna.com/api/v1/tts", {
  method: "POST",
  headers: {
    Authorization: "Bearer sautna_sk_...",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ voice: "zol", text: "أهلين! كيف اخبارك؟" }),
});
if (!res.ok) throw new Error((await res.json()).error.message);
await Bun.write("speech.wav", await res.arrayBuffer()); // or fs.writeFile

Streaming · recommended for anything interactive

With "stream": true the response is raw 24 kHz s16le mono PCM, one chunk per rendered sentence — playback can start as soon as the first sentence lands instead of waiting for the whole text. This is how our own phone lines run. For IVR, resample to 8 kHz or request format=ulaw_8000 on a non-streamed call.

curl — streaming

# Streaming: raw 24 kHz s16le PCM; audio is sent as the model generates it
curl -N -X POST https://sautna.com/api/v1/tts \
  -H "Authorization: Bearer sautna_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"voice": "zol", "text": "أهلين! كيف اخبارك؟", "stream": true}' \
  --output speech.pcm
# play it: ffplay -f s16le -ar 24000 -ch_layout mono speech.pcm

safe retries

# Reuse the SAME key only when retrying this exact request
Idempotency-Key: assistant-turn-0184

# Retry 429 after Retry-After; retry 502/504 with exponential backoff.
# Never reuse one key for different text, voice, format, or stream settings.

Pipecat integrations can wrap each received chunk in a TTSAudioRawFrameat 24 kHz, one channel. A ready-to-adapt service example is available at examples/pipecat_sautna_tts.py.

API limits

Community accounts do not include API access. API access is invite-only and can be granted by Sautna to selected accounts.

Monthly, per-request and rate limits are account-specific. Approved users can see their exact entitlements inside Studio → Plan and current rate-limit values in API response headers. To discuss approved API access, email hello@sautna.com.

Errors

StatusCodeMeaning
401missing_api_key / invalid_api_keyNo key sent, or the key was revoked.
402quota_exceededMonthly character quota used up — wait for reset.
403api_not_includedAPI access is invite-only.
404unknown_voiceThe voice id doesn't exist.
413text_too_longText exceeds your plan's per-request limit.
429rate_limitedToo many requests per minute — back off and retry.
502/504synthesis_failedUpstream synthesis error or timeout — safe to retry.

Latency

Synthesis runs on an always-on GPU — there are no cold starts. Repeated sentences are served from cache (see the X-Cache header) and return instantly. Non-streamed requests render and quality-check the full audio before responding, so long texts take longer end-to-end — prefer stream: true when a listener is waiting. Set client timeouts around 60 seconds for long non-streamed texts.