Operations

Errors

Every error code the API returns, what causes it and what to do next.

Errors use conventional HTTP status codes and a consistent JSON body. Branch on error.code, which is stable; message is for humans and may change.

Error object

{
  "error": {
    "type": "billing_error",
    "code": "insufficient_words",
    "message": "Your allowance is used and pay as you go is off, or its monthly cap is reached. Nothing was processed. This request needs 6,120 words; 2,400 are left in this period's allowance.",
    "request_id": "req_01JD4R9QT2",
    "doc_url": "/docs/errors#insufficient_words"
  }
}

Error codes

StatuscodeCauseWhat to do
400invalid_requestA parameter is missing, has the wrong type or is out of range. error.param names it.Fix the request. Don't retry unchanged.
400text_too_shortDetect input under 40 words, or Rewrite input under 20 words.Send more text, or skip detection for short snippets.
401invalid_api_keyMissing, malformed, revoked or expired key.Check the Authorization header and key mode.
402insufficient_wordsYour allowance is used and pay as you go is off, or its monthly cap is reached. Nothing was processed. Also returned when the workspace has no plan.Upgrade, or switch on pay as you go (or raise its cap) in Dashboard → Billing.
403permission_deniedThe key lacks a scope, a test key touched a live resource, or the feature needs a higher plan (deep strength, calibrated Detect, more voices).Use a key with the scope in error.param, or upgrade.
404not_foundUnknown voice, job or endpoint, or it belongs to another workspace.Check the id and the key's workspace.
409idempotency_conflictSame Idempotency-Key reused with a different body.Use a new key per distinct request.
410result_expiredThe job's result was already delivered (sync response, an earlier GET, the event stream or a webhook) or wasn't collected within 60 minutes, so it was deleted.Save the result the first time you receive it. Run the request again if you need it (that is billed again).
413text_too_longInput over 25,000 words.Split the document or contact us for higher limits.
422fidelity_failedStrict mode: a protected fact (number, unit, name, link, quote) changed in the output. Not billed.Retry once, then route to a person.
422platform_constraintThe text can't fit the platform preset without dropping a protected fact.Shorten the source or use another platform.
429rate_limitedPer-key request, concurrency or word limit exceeded.Wait for Retry-After, then back off.
500internal_errorSomething failed on our side.Retry with backoff; quote request_id to support.
503overloadedTemporary capacity limit.Retry with backoff, or use async jobs.
503capacity_unavailableAn accepted job could not finish in time (after repeated waits, or 120 minutes after it was accepted) because model capacity stayed unavailable. Reported on the job and its job.failed webhook. Nothing was billed and your text was deleted.Submit the job again later.

422 fidelity_failed

The one error that is part of normal operation. It means the system worked: a fact changed during rewriting and we refused to return it.

{
  "error": {
    "type": "fidelity_error",
    "code": "fidelity_failed",
    "message": "1 protected fact changed between source and output. Nothing was billed.",
    "request_id": "rw_01JD4R2N7P",
    "changed_facts": [
      {
        "type": "number_unit",
        "source": "14 days",
        "output": "14 weeks"
      }
    ]
  }
}

See Fidelity checks for the rules.

What to retry

  • Retry with backoff: 429, 500, 503, and network timeouts.
  • Retry once, then escalate: 422 fidelity_failed.
  • Don't retry: other 4xx codes. The same request will fail the same way.

Note:

Send an Idempotency-Key header on POST requests so a retry after a timeout can't bill you twice. A replay answers from the original job (we never store response text): an async request gets the job; a sync request gets its result if the first response never reached you, otherwise 410 result_expired.