{
"error": {
"message": "...",
"type": "...",
"code": "...",
"param": null
}
}
400 — invalid request
| Code | Returned when |
|---|---|
invalid_json | Request body is not a JSON object. |
transcript_too_long | Pronunciation-dictionary alias expansion pushed the transcript past 20,000 characters (4× the 5,000-character submit cap). |
invalid_output_format | stream: true with an mp3 container. Message: “Streaming TTS does not support mp3; use wav or pcm/raw.” |
invalid_generation_config | stream: true with generation_config.speed ≠ 1. |
unsupported_audio_format | STT file is not raw PCM16 or WAV — mp3, ogg, flac, mp4, and m4a are rejected by magic bytes. |
invalid_sample_rate | sample_rate outside 8000–48000 or not in the model’s allowlist. |
invalid_turn_detection | turn_detection is not balanced, patient, or responsive. |
upgrade_required | Plain HTTP GET to /stt/websocket without a WebSocket upgrade. |
401 — authentication
| Code | Returned when |
|---|---|
invalid_api_key | No key on the request (“You didn’t provide an API key. Get one at https://withhopper.com/dashboard and pass it via the Authorization header: ‘Authorization: Bearer sk_hopper_…’.”) or the key does not exist (“Incorrect API key provided. Check your key at https://withhopper.com/dashboard.”). |
api_key_disabled | The key was revoked. Message: “This API key has been disabled.” |
402 — out of credits
| Code | Returned when |
|---|---|
insufficient_quota | Credit balance is zero on any metered route, including STT WebSocket upgrade. Message: “You have run out of credits. Contact us to add more.” A live STT session that hits zero closes with WebSocket code 1008. |
403 — forbidden
| Code | Returned when |
|---|---|
voice_read_only | Mutating a platform voice. Platform voices are read-only; only your own voices can be renamed, retagged, or deleted. |
404 — not found
| Code | Returned when |
|---|---|
model_not_found | The model id does not resolve to an active model of the route’s modality. |
voice_not_found | The voice does not exist or is not visible to your org. |
dict_not_found | pronunciation_dict_id does not match one of your dictionaries. |
409 — conflict
| Code | Returned when |
|---|---|
voice_profile_missing | The voice has no ready profile for the requested model (“Voice ‘X’ is not prepared for model ‘Y’.”). Prepare one with POST /voices/:id/profiles. |
413 — payload too large
| Code | Returned when |
|---|---|
body_too_large | Request body exceeds the route cap: 10 MB on /v1/* LLM routes, 25 MB on /v1/audio/transcriptions. |
429 — rate limited
| Code | Returned when |
|---|---|
rate_limit_exceeded | More than 600 requests in the key’s fixed one-minute window. Message: “Rate limit reached: 600 requests per minute per key. Retry after Ns.” Comes with a Retry-After: <seconds> header — see Rate limits. |
5xx — server errors
| Status | Code | Returned when | Retry |
|---|---|---|---|
| 500 | internal_error | Unhandled gateway exception. | Retry once; persistent 500s are a gateway fault, not your request. |
| 502 | upstream_unavailable, upstream_error | The model upstream is unreachable, timed out, or returned an error. | Retry with backoff. |
| 503 | verify_unavailable | Key verification is temporarily down. Message: “Key verification is temporarily unavailable. Retry shortly.” | Retry within seconds; your key is fine. |
| 503 | model_offline | The model has no healthy upstream. | Back off in minutes, not milliseconds — tight retries do not bring the model back. |
| 504 | upstream_timeout | The upstream exceeded the 300 s deadline. | Retry with a shorter request; a full 300 s timeout rarely resolves on replay. |