Python OpenAI SDK → Audra TTS (copy-paste) — Audra
The Python OpenAI SDK works against Audra unchanged: one base_url argument and you keep the same client, retries, and streaming helpers your service already has. This is the production checklist — what to add around that one-line swap so the integration survives retries, timeouts, and deploys.
The swap
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AUDRA_API_KEY"],
base_url="https://audratalks.com/v1",
)
resp = client.audio.speech.create(
model="audra-essential",
voice="june",
input="Chapter one. The river was low that autumn.",
)
resp.stream_to_file("ch1.mp3")
Production checklist
- Timeouts: set a per-request httpx timeout of at least 3600s — with client.with_options(timeout=...). A ~500-char render takes 1–3 minutes; 50k-character renders 30–90+. The SDK default will give up long before.
- Retries: client.audio.speech.create on /v1 routes through the compat shim to POST /v2/speech, which honors Idempotency-Key. Generate one UUID per logical render (not per attempt), pass it in extra_headers, and let the SDK's max_retries=3 retry safely without double billing.
- Env: never hardcode the key — AUDRA_API_KEY from env, rotated at /dashboard/keys. Key rotation is limited to 5/hour per account.
- Chunking: over ~8,000 characters, sync requests are auto-queued as background jobs unless you send X-Audra-Sync: true. For chapter-scale work use the native jobs API (POST /v2/speech/jobs, up to 500k chars, webhook_url) instead of the SDK shim.
- Error classes: catch openai.RateLimitError (respect Retry-After; free keys get 60 RPM, paid 120) and openai.AuthenticationError separately — one is backoff, the other is a deploy blocker.
Streaming to file vs bytes
from openai import OpenAI
client = OpenAI(api_key=..., base_url="https://audratalks.com/v1")
# Streamed: writes incrementally, safe for long renders on small workers
with client.audio.speech.with_streaming_response.create(
model="audra-essential",
voice="june",
input=script,
response_format="mp3",
) as response:
response.stream_to_file(f"episode_{ep}.mp3")
# Or buffer in memory when the file goes straight to S3
buf = client.audio.speech.create(
model="audra-essential", voice="june", input=script,
).content
s3.put_object(Bucket="narration", Key=f"{ep}.mp3", Body=buf)
The JS-equivalent snippet and the full v1-to-v2 field map (input vs text, response_format vs format) live in /blog/openai-sdk-tts-migration — read that if you run Node services too.