Quickstart
Generate Arabic speech with cURL or the OpenAI Python SDK and save a playable WAV.
1. Create an API key
Open API Keys, click Create key, and copy the key. Configure SAWTAK_API_KEY using the authentication setup.
2. Choose a voice
curl --fail --show-error https://api.sawtakarabi.ai/v1/voices \
-H "Authorization: Bearer $SAWTAK_API_KEY"Choose an entry in data with status: "ready" and copy its id. See Voices for dialect filters and audio previews.
3. Generate speech
Speech generation uses your Sawtak Arabi balance. Replace <VOICE_ID> with the ID you selected. To save a playable file with completion checks, use the Python example below.
The cURL example receives a WAV stream and saves its response headers:
curl --fail --show-error https://api.sawtakarabi.ai/v1/audio/speech \
-H "Authorization: Bearer $SAWTAK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"voice": "<VOICE_ID>",
"input": "مرحباً بك",
"response_format": "wav"
}' \
--dump-header speech.headers \
--output speech.partial.wavKeep the X-Request-Id in speech.headers. Before using the audio, check the operation for outcome: "completed" and confirm its completion_bytes matches the saved PCM payload, excluding the WAV header. Then finalize the file’s length fields for standard media players. Finalizing a WAV does not establish that generation completed.
See Text to speech for optional settings and Errors if a request fails. Using Claude Code or Codex? The official skill handles voice selection and creates a finalized WAV for you.
Python: save a playable WAV
Use the official OpenAI Python SDK with Sawtak Arabi's base URL and your Sawtak Arabi API key. The SDK is the client in this example; speech is generated by Sawtak Arabi's model. This example requires Python 3.10+ and uses Python's built-in wave module to finalize the file, so FFmpeg is unnecessary.
python3 -m venv .venv
source .venv/bin/activate
python -m pip install 'openai>=1.102.0,<3'
export SAWTAK_VOICE_ID='<VOICE_ID>'Replace <VOICE_ID> with the ready voice selected above. Save this as speech.py and run python speech.py in the terminal where you configured SAWTAK_API_KEY. Run it on your server or computer; keep the key out of browser code. Generation uses your Sawtak Arabi balance.
import os
import time
import uuid
import wave
from pathlib import Path
from urllib.parse import quote
from openai import OpenAI
output = Path("speech.wav")
partial = Path("speech.partial.wav")
if output.exists() or partial.exists():
raise SystemExit("Reuse the existing audio or choose new output filenames.")
with OpenAI(
api_key=os.environ["SAWTAK_API_KEY"],
base_url="https://api.sawtakarabi.ai/v1",
max_retries=0,
timeout=180.0,
) as client:
with partial.open("xb") as file, wave.open(file, "wb") as wav:
wav.setparams((1, 2, 24000, 0, "NONE", "not compressed"))
received = 0
pending = b""
with client.audio.speech.with_streaming_response.create(
model="arabic-tts-1",
voice=os.environ["SAWTAK_VOICE_ID"],
input="مرحباً بك في صوتك عربي.",
response_format="pcm",
extra_body={"sample_rate": 24000},
extra_headers={"Idempotency-Key": str(uuid.uuid4())},
) as response:
operation_id = response.headers.get("x-request-id")
print("Operation:", operation_id, flush=True)
if (
response.headers.get("content-type", "").split(";")[0] != "audio/pcm"
or response.headers.get("x-sample-rate") != "24000"
or response.headers.get("x-channels") != "1"
):
raise RuntimeError("Unexpected audio format; inspect the operation.")
for chunk in response.iter_bytes():
pending += chunk
size = len(pending) - len(pending) % 2
wav.writeframes(pending[:size])
received += size
pending = pending[size:]
if not received or pending or not operation_id:
raise RuntimeError("Incomplete audio or missing operation ID; keep the partial file.")
for attempt in range(5):
operation = client.get(
"/operations/" + quote(operation_id, safe=""), cast_to=object
)
if operation["state"] == "terminal":
break
time.sleep(1)
if operation.get("outcome") != "completed" or operation.get("completion_bytes") != received:
raise RuntimeError("Completion unconfirmed; keep the partial file and inspect the operation.")
os.link(partial, output)
partial.unlink()
print(f"Saved {output}: {received / (24000 * 2):.2f} seconds")The SDK streams raw PCM; wave writes a standard mono, 16-bit, 24 kHz WAV. The script handles chunks that split an audio sample and promotes the partial file only after the operation reports completion with the expected byte count. It refuses to replace existing audio and disables automatic speech retries.
If it stops, keep speech.partial.wav and the printed operation ID. Follow Operations and retries before submitting again: a partial recording may already be charged, and another run starts a new request. A pending or unavailable status does not prove failure. For narration workflows with saved request metadata and recovery commands, use the official skill.
Pronunciation enhancement is off by default. To enable it, add "enhance_pronunciation": True to extra_body; it is separate from the default text normalization. Choose a voice for your intended dialect and listen to a short representative script before integrating longer content. See text preparation and streaming playback for the next steps.