Documentation TTS API: turn docs into listen-mode audio — Audra
Docs sites have a listen-mode problem: users want runbooks and changelogs on headphones, but nobody re-records audio every time a page changes. The answer is to treat docs audio as build output — rendered in CI from markdown, on every merge, at batch economics.
This post is the docs-to-audio pipeline: page-level renders, the format tradeoff for inline players, voice choice for technical prose, and the cost of narrating a whole docs site every time it changes.
The persona: a 300-page docs site built on every merge
Assume 300 pages averaging 2,500 characters after stripping code blocks and nav — about 750,000 characters total. Each page is a separate render, because pages are the natural unit: they map to URLs, they change independently, and partial rebuilds only re-render dirty pages. Every page fits the 50k sync limit; the interesting engineering is the build hook, not the API call.
Render per page, in CI
Strip code fences and tables from the markdown, keep headings as natural pause points (paragraph breaks already produce sentence pauses), and POST the prose to /v2/speech. Store the MP3 beside the static HTML with the page hash in the filename; skip re-render when the hash is unchanged.
curl
curl https://audratalks.com/v2/speech \
-H "Authorization: Bearer sk-audra-..." \
-H "Content-Type: application/json" \
-d '{"model":"audra-essential","voice":"ellis","text":"Rate limiting...","format":"mp3"}' \
--output page-audio.mp3
Python
import requests
resp = requests.post(
"https://audratalks.com/v2/speech",
headers={"Authorization": "Bearer sk-audra-..."},
json={"model": "audra-essential", "voice": "ellis", "text": page_text, "format": "mp3"},
timeout=3600,
)
resp.raise_for_status()
open("page-audio.mp3", "wb").write(resp.content)
JavaScript
const res = await fetch("https://audratalks.com/v2/speech", {
method: "POST",
headers: { Authorization: "Bearer sk-audra-...", "Content-Type": "application/json" },
body: JSON.stringify({ model: "audra-essential", voice: "ellis", text: pageText, format: "mp3" }),
});
if (!res.ok) throw new Error(await res.text());
await writeFile("page-audio.mp3", Buffer.from(await res.arrayBuffer()));
At 60 requests/minute per key on free tiers (120 paid), 300 pages is a five-minute rebuild — throttle to one request per second and you stay far under the 429 line. Read X-QA-Passed per page so a failed render fails the build rather than shipping silence.
Format: small pages, small files
A 2,500-character page is about 3 minutes of audio — roughly 2.9 MB as MP3 at 128 kbps. WAV would be around 30 MB per page, absurd for an inline player. Request mp3 (the default) and let the browser stream it. The mp3-vs-wav post has the full math if your pipeline differs.
Voice: technical prose is its own genre
Docs read differently from marketing copy. ellis handles dense technical prose without sounding bored; grant gives newsread-style clarity for reference pages. Whatever you pick, one voice for the whole site — listen mode is a feature, and features are consistent. CLI flags and error codes should be rewritten for the ear or wrapped in <say-as interpret-as="characters"> rather than spoken raw.
Cost: narrating the whole site, every merge
A full-site rebuild is 750,000 characters × $25/1M ≈ $18.75 once. But merges typically dirty 1–10 pages (2.5k–25k chars), so an average rebuild costs $0.06–$0.60. A site merging 20 times a day with about 4 dirty pages each narrates for roughly $0.50 a day, and hash-deduplication drops that further. Run your own numbers on /calc.
Watch-outs for docs sites
- normalize (default true) expands URLs and emails into words — right for listen mode, wrong for pages that teach literal URLs; toggle per page.
- Versioned docs: encode the version in the audio filename, not the URL, or old audio gets orphaned in the player cache.
- Trial is evaluation-only; ship the listen button on a paid pack and send X-Audra-Commercial-Use: true.
Word-timed captions for a read-along highlight UI: the captioned speech post. Pronunciation overrides for product names: the per-account lexicon post.