YarnGPT
Developer quickstart

Your first Nigerian‑accented audio in 5 minutes

Five steps from an empty terminal to a saved .mp3 — create a key, pick a voice, and stream your first synthesis. Every request below uses a Bearer API key.

Base URL
yarngpt.ai
Auth
Bearer API key
Key format
sk_live_…
1

Create an API key

Sign in with Google, then open your Account page and hit Create key. Copy the full secret the moment it's shown — it looks like sk_live_<id>.<secret> and is revealed exactly once.

Treat the key like a password. Anyone holding it can spend your credits — if it leaks, deactivate it from Account and mint a new one.

2

Pick a voice

GET /api/v1/voices needs your Bearer key like everything else here — it lists every available voice; use the name field as your voice value.

curl -H "Authorization: Bearer sk_live_XXXX.XXXXXXXXXXXX" https://yarngpt.ai/api/v1/voices

There's no fixed list to print here — the response above is the only source of truth, and it differs by environment and changes over time. Read name from it directly rather than hardcoding a voice id in production; one entry has default: true if you just want something reasonable to start with.

3

Make your first request

POST /api/v1/tts queues an asynchronous job — it returns a job_id immediately, not audio. Poll GET /api/v1/status/{job_id} until completed, then download audio_url. Send an Idempotency-Key so a retry returns the original job instead of billing you twice.

curl -X POST "https://yarngpt.ai/api/v1/tts" \
  -H "Authorization: Bearer sk_live_XXXX.XXXXXXXXXXXX" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "text": "Ẹ kú àárọ̀. Welcome to YarnGPT.",
    "voice": "umar",
    "output_format": "mp3"
  }'
# → {"job_id":"...","status":"queued","accepted_at":"..."}

# poll until status is "completed", then download audio_url
curl "https://yarngpt.ai/api/v1/status/<job_id>" -H "Authorization: Bearer sk_live_XXXX.XXXXXXXXXXXX"
curl "<audio_url>" --output speech.mp3
FieldRequiredNotes
textYesNon‑empty after trimming. Max 15,000 characters.
voiceNoA name from /api/v1/voices — exact, case-sensitive. Omit to use whichever entry has default: true.
output_formatNomp3 (default), wav, pcm, opus, or flac.
target_languageNoTranslate the text before synthesising it — one of 18 codes: am ar de en es fr ha ig lg pt rw sn sw tw wo xh yo zu. Omit to speak the text as written. Billed on the translated text, which can run longer or shorter than the original.

An unrecognized field still 422s. Limited to 120 requests/day per key.

4

Read the response & handle errors

Success from POST /api/v1/tts is 202 JSON — a job to poll, not audio (see step 3). Every error, from every endpoint, has the same shape: {"error":{"code","message","service","trace_id","details"}}. Log trace_id — it's what lets anyone find the request server-side.

StatuscodeMeansDo
400INVALID_INPUTMissing Idempotency-Key or similar preconditionCheck required headers.
401—Missing / invalid / revoked keyCheck the Bearer header and that the key is active.
402QUOTA_EXCEEDEDOut of creditsTop up, then retry. details carries credits_remaining/credits_required.
404VOICE_NOT_FOUNDUnknown voice idCheck the name against /api/v1/voices — exact, case-sensitive.
422VALIDATION_ERRORBody failed schema validationRead details.errors — often empty/too-long text or an unrecognized field.
400TEXT_TOO_LONGText expands into more chunks than the server's cap, after numbers/currency are spelled out server-sideRead details.chunk_cap — it differs per environment, don't hardcode it. Split the text and resubmit; don't retry as-is.
429RATE_LIMITEDRate limit hitBack off — respect retry-after.
503SERVICE_UNAVAILABLEA downstream dependency is downTransient — details.dependency names it. Safe to retry.

service in the error tells you who rejected the request. If it reads nginx-ingress, the request never reached a service at all — that's the gateway itself (an auth or edge rate-limit problem), not application logic.

Checks run voice first, then text — an unknown voice combined with over-length text still comes back 404 VOICE_NOT_FOUND, not 400.

5

Go realtime (optional)

Building a voice agent? POST /api/v1/streaming/conversation returns raw audio directly — no job_id, no polling. It's not saved and not idempotent by default (an optional Idempotency-Key makes a retry of the same turn free, up to 5 times within 450 seconds), and text is capped shorter — 1–5,000 characters instead of 15,000. There's no published per-endpoint rate limit for it beyond the developer-API edge limit (40 req/s, burst 80 — per calling client since release v0.7.0, not pooled across every caller). Use output_format: "pcm" (raw signed 16-bit little-endian mono) for a telephony pipeline with no decode step, or mp3/wav for a container.

The sample rate isn't fixed — don't hardcode one. For pcm, read it from the response's Content-Type header (audio/l16; rate=<N>; channels=1) and hand that number to your decoder; a client that assumes a fixed rate plays back at the wrong speed and pitch, with no error to catch. This route can also return 400 TEXT_TOO_LONG (see step 4) or, on a total provider outage, a 200 with a zero-byte body — check the byte count before treating a response as audio.

curl -N -X POST "https://yarngpt.ai/api/v1/streaming/conversation" \
  -H "Authorization: Bearer sk_live_XXXX.XXXXXXXXXXXX" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "How may I help you today?",
    "voice": "umar",
    "output_format": "pcm"
  }' \
  --output reply.pcm

You're shipping

That's the whole loop. Dig into the full reference for every field and endpoint, or reach out when you're ready for production limits.