Skip to content

Errors

All API errors share the same envelope:

json
{
  "error": {
    "code": "rate_limited",
    "message": "request rate exceeded (60/min)",
    "details": { "retry_after_seconds": 12 }
  }
}

code is a stable machine-readable identifier; message is a friendly string suitable for surfacing in logs but not for branching. details is endpoint-specific — see the table below.

HTTP status codes

StatusMeaning
200OK.
201Resource created (file finalized, key issued).
202Accepted — conversion queued.
204No content (delete, revoke).
302Redirect to a presigned download URL (/v1/conversions/{id}/output).
400Bad request — validation, unsupported format, unknown preset.
401Missing / invalid / revoked API key.
403Forbidden — wrong scope, free tier, banned account.
404Resource not found OR not yours.
413Payload too large (file exceeds tier or scanner cap).
422Magic-byte mismatch or malware detected.
429Rate limited; honour Retry-After.
503Maintenance mode — try again shortly.
5xxServer error — safe to retry once with jittered backoff.

Error code reference

CodeStatusRetryableNotes
unauthorized401NoMissing / invalid / revoked key.
forbidden403NoWrong scope, free tier, banned.
not_found404NoIncludes "doesn't belong to caller".
rate_limited429After Retry-AfterPer-key request bucket.
conversion_rate_limited429After Retry-AfterPer-key conversion bucket.
file_too_large400 / 413NoTier cap.
mime_mismatch422NoDeclared MIME ≠ detected MIME.
malware_detected422NoClamAV hit; object deleted, audit log written.
scan_size_limit_exceeded413NoFile exceeds clamd StreamMaxLength.
unsupported_pair400NoNo conversion path from source → target.
unknown_preset400NoPreset not allowed for this pair.
bulk_too_large400NoExceeds tier.bulk_max.
not_ready400Yes (poll)Hit /output before completed.
not_cancellable400NoConversion already terminal.
unknown_scope400NoScope name not recognised at create time.

Worker-side conversion failures

If a conversion finishes with status: 'failed', look at error_code in the response body:

FieldMeaningShould retry?
malwareClamAV hit during finalize.No.
unsupportedWorker tool can't read the input.No.
timeoutWall-clock timeout in the worker.Maybe — try a smaller input or different preset.
tool_errorWorker tool returned a fatal error (libreoffice crash, etc.).No.
permanentCatch-all for terminal worker errors after retries.No.
transientWorker exhausted its 2 auto-retries with retryable errors (network, OOM).Yes — create a new conversion.

There's no manual "force retry" on the public API; if you want one, create a new conversion with the same input + preset.

Built solo on dedicated metal.