API errors

Use this guide to handle Sonilo API failures, authentication problems, rate limits, and async task errors.

Error format

Non-streaming errors return JSON with a stable code and a human-readable message. Use the HTTP status for retry and billing logic; use code for user-facing handling.

{
  "code": "rate_limit_exceeded",
  "message": "Rate limit exceeded: your account allows 60 requests per minute. Please retry after 1 minute. To raise your limit, please contact [email protected]."
}

Status codes

CodeCondition
400Invalid request shape, conflicting inputs, missing required fields, or unsupported parameters.
401API key is missing, invalid, revoked, or not sent as Authorization: Bearer sk_your_api_key.
402Branch on the code field. trial_exhausted means the free trial for that service is spent and the account has never been funded — add a payment method. insufficient_balance means a funded wallet ran out — add balance and retry. payment_required covers the remaining cases, such as a suspended account or an exceeded credit limit.
403API key is valid, but the account or workspace cannot access this endpoint.
413Uploaded file exceeds the endpoint size limit.
422The input is syntactically valid but cannot be processed, such as an over-limit duration or a source with no video track.
429Requests-per-minute limit or concurrent-generation limit exceeded. The message says which one, so a client can tell whether to slow down or to wait for a slot. Both limits can be raised — contact [email protected].
502Upstream generation provider failed or timed out. Retry the request.

Rate limit errors

Two separate limits return 429 with rate_limit_exceeded, and they call for different handling. The message distinguishes them; the code is the same for both.

The per-minute limit — slow down, then retry:

{
  "code": "rate_limit_exceeded",
  "message": "Rate limit exceeded: your account allows 60 requests per minute. Please retry after 1 minute. To raise your limit, please contact [email protected]."
}

The concurrency limit — wait for a running generation to finish, since no amount of waiting frees a slot on its own:

{
  "code": "rate_limit_exceeded",
  "message": "Too many concurrent generations: 5 of 5 in progress. Please wait for one to finish before starting another. To raise your limit, please contact [email protected]."
}

The numbers in both messages are your account's actual limits — the values above are the standard-tier defaults. See GET /v1/account/services to read them programmatically, and email [email protected] to have either raised.

Video input errors

Every endpoint that takes video or video_url — /v1/video-to-music, /v1/video-to-video-music, /v1/video-to-sfx, /v1/video-to-video-sfx, /v1/video-to-sound, /v1/video-to-video-sound, and /v1/dubbing — requires a source that carries a real video stream. An audio-only file returns 422 before anything is generated or billed.

{
  "code": "unprocessable_entity",
  "message": "This file has no video track. video-to-* endpoints need a real video; provide a video file or a URL that serves one."
}

The container matters less than what is inside it. Renaming an .m4a or .mp3 to .mp4does not add a video stream, and an audio file's embedded cover art does not count as one — it is a single still frame, which the API excludes deliberately. Send a different file; retrying the same one cannot succeed.

A source that does carry video but runs past the endpoint's limit returns the same status with a different message, naming both durations:

{
  "code": "unprocessable_entity",
  "message": "Video duration 370.0s exceeds maximum of 360s"
}

Eval verification

For generation smoke tests, mark the run as passed only when the expected output file exists and is larger than 0 bytes. For the text-to-music minimal workflow, require output.m4a after the complete stream event. Do not create placeholder media files after failed API calls.

When HTTP 402 returns trial_exhausted, the account has used every free trial call for that service and has never added funds. Prompt the user to add a payment method rather than retrying. When it returns insufficient_balance, a funded wallet ran dry — add balance and retry the same request. Branch on code, never on the message text.

Authentication failures

Send Authorization: Bearer sk_your_api_key on every API request to https://api.sonilo.com/v1. Store real keys server-side, commonly as SONILO_API_KEY.

curl https://api.sonilo.com/v1/account/services \
  -H "Authorization: Bearer sk_your_api_key"

Use 401 for missing or invalid credentials. Use 403 for a valid key that lacks access to the requested endpoint or workspace. In coding-agent evals, classify these as auth_invalid and forbidden, not as Sonilo endpoint bugs.

Streaming errors

/v1/text-to-music and /v1/video-to-music stream application/x-ndjson. Read one JSON object per line. If an event has type: "error", stop reading and handle the included code and message.

{
  "type": "error",
  "code": "upstream_error",
  "message": "Upstream processing error. Please retry."
}

Async task errors

/v1/text-to-sfx, /v1/video-to-sfx, and /v1/audio-ducking return 202 with a task_id. Poll GET /v1/tasks/{task_id} until the task is succeeded or failed.

{
  "task_id": "9f5f2f7e-...",
  "status": "failed",
  "error": {
    "code": "upstream_error",
    "message": "Upstream processing error. Please retry."
  },
  "refunded": true
}

Retry guidance

  • Retry 429 and transient 502 failures with exponential backoff. When the response includes Retry-After, wait at least that many seconds before the next attempt.
  • For the per-minute limit specifically, the counter runs on a fixed 60-second window and every request counts, rejected ones included. A retry inside the same window cannot succeed, which is why the message asks for a full minute: that clears the window whatever your phase within it.
  • Do not retry 400, 401, 403, or 422 until the request, key, permissions, or input changes.
  • Use 404 / not_found to catch wrong hosts or paths. Requests must go to https://api.sonilo.com/v1, not docs URLs such as https://platform.sonilo.com/docs/api/video-to-sfx or local proxy routes such as /api/dashboard.
  • For endpoint parameters and examples, start from the API reference.