Errors and limits

Every error is JSON with a stable code. What each one means and what to do about it.

Errors come back with an HTTP status and a JSON body:

json
{
  "error": {
    "code": "unknown_voice",
    "message": "Voice `kate` isn't available for model `v1`. See GET /v1/voices."
  }
}

Branch on code, which stays the same; message is written for people and may change.

Error codes

StatusCodeMeaningWhat to do
400invalid_requestThe body isn't valid JSON or has the wrong shape.Fix the request; the message says what's wrong.
400empty_texttext is empty, or only whitespace.Send some text.
400text_too_longtext is over 5,000 characters.Split it into several requests, ideally at sentence boundaries.
400unknown_voiceNo such voice for that model, or a cloned voice from another account.Check the id with GET /v1/voices.
400invalid_temperaturetemperature is outside 0 to 2.Use a value in range, or leave it out.
400invalid_languagelanguage isn't one of en, es, fr, de, ja, zh.Use one of those, or leave it out.
401unauthorizedThe API key is missing, wrong or revoked.Send Authorization: Bearer io_live_… with a current key.
402insufficient_creditsYour credit balance is used up.Add credit or turn on auto-recharge in Billing.
404not_foundNo such endpoint, or a cloned voice that doesn't exist.Check the path and id.
502upstream_error, upstream_unreachableGeneration failed on our side. You aren't charged.Retry with backoff.
503busyEvery slot for that model was taken for 3 seconds.Retry with backoff.
500internalSomething broke on our side.Retry; if it keeps happening, tell us the X-Request-Id.

Speech to text adds invalid_response_format and, like cloning, missing_audio, invalid_audio and audio_too_long.

Cloning adds its own codes, all 400: consent_required, invalid_name, invalid_transcript, missing_audio, invalid_audio, audio_too_short, audio_too_long and too_many_voices. Each message says exactly what to fix.

Retrying

Retry 502, 503 and 500 with exponential backoff and jitter, for example after 0.5 s, 1 s, 2 s and 4 s. Don't retry 4xx errors unchanged: they fail the same way every time.

python
import random, time, requests

def tts(payload, key, attempts=5):
    for i in range(attempts):
        r = requests.post("https://api.iodine.dev/v1/tts", json=payload,
                          headers={"Authorization": f"Bearer {key}"})
        if r.status_code not in (500, 502, 503):
            r.raise_for_status()
            return r.content
        time.sleep(0.5 * 2 ** i + random.random() * 0.25)
    r.raise_for_status()

Limits

Text per request5,000 characters
Waiting for a free slotup to 3 seconds, then 503 busy
Realtime idle timeout5 minutes without a message
Realtime message size256 KB
Cloned voice recording3 to 30 seconds, up to 20 MB
Cloned voices per account50
Speech to text upload25 MB, 10 minutes

Need more throughput or higher limits? Email business@iodine.dev.