Skip to main content

API docs → Errors

Error reference

Every error returned by the API. error is the stable machine code — switch on it, never on message (which is for humans and may change).

Response shape

Every error follows the same envelope:

{
  "error":      "rate_limit_exceeded",   // stable machine code
  "message":    "Monthly limit reached: 5 detections.",
  "request_id": "0190b1f8-aae5-7e3a-9b1c-7f4ba2c0d3e1",
  "details": {                           // optional, per-error context
    "used":               50,
    "limit":              50,
    "resets_at":          1717948800000,
    "retry_after_seconds": 28800
  }
}

Always include request_id in support tickets — we use it to pull every log line and downstream call for that request.

Codes

HTTPCodeCauseFix
400invalid_inputBody or query parameter failed Zod validation. Common: text below minLength, strictness outside enum, mc_k out of range.Inspect `details` — Zod returns the exact field + message. Re-send with the right shape.
401unauthorizedMissing `Authorization` header, malformed token, revoked or unknown key.Confirm the header is `Authorization: Bearer dad_live_...`. Generate a fresh key in your dashboard if needed.
402upgrade_requiredWord count exceeds tier max, or the endpoint (deep / plagiarism) requires a paid tier.Truncate the input, or upgrade. The error includes `upgrade_url` and `upgrade_features` so you can hand the user a deep link.
402word_limit_exceededPer-document word limit hit. Anonymous / free = 1,000 words. Pro/Plus = 5,000. Team = 20,000.Split into chunks (recommended: paragraph boundaries) or upgrade.
402paid_tier_requiredYou hit a Pro+ endpoint from a free / anonymous tier.Upgrade. The response includes a `feature_summary` outlining what unlocks.
413payload_too_largeRequest body exceeded 200KB.Trim the body — 5,000 words is roughly 25-30KB so this only fires on misuse.
422insufficient_textInput has fewer than 80 words. Below this threshold the prior dominates the score, so we refuse rather than return a misleading number.Paste more text. `min_words` and `recommended_min_words` are in the response.
429rate_limit_exceededPer-tier quota exhausted. Anonymous = 1/calendar-month/IP. Registered free = 5/calendar-month. Paid tiers are unmetered (Pro / Team / Enterprise) or balance-gated (PAYG / API-only).Wait until `resets_at` (unix ms). The SDKs handle this automatically with backoff. The `options` array in the response gives upgrade paths the user can pick from.
429rate_limitedAlternate code returned on some endpoints. Identical semantics to `rate_limit_exceeded`.Same as `rate_limit_exceeded`.
500internal_errorUnhandled server-side exception. The response is intentionally opaque (no stack traces leak).Retry with exponential backoff (max 3 tries). Include the `request_id` in any support ticket.
502deep_scan_failedML upstream (Modal) unreachable or returned an error. Only fires on `/v1/detect/deep`.Retry. Status page (https://deepaidetector.com/status) shows when this is degraded.
503billing_not_configuredStripe keys not configured (staging / fresh installs only). Should never fire in production.Contact support if you see this — internal config bug.
503service_unavailableAPI process unhealthy. Usually transient.Retry with backoff. Check /status.
404not_foundResource lookup miss (e.g. `/v1/plagiarism/scans/:id` with an unknown id).Double-check the id. Ids are UUIDs returned by the originating endpoint.

Retry-safety matrix

HTTPRetry?Strategy
400NoFix the request and re-send.
401NoRotate / regenerate the key.
402NoUpgrade or change the input.
413NoShrink the body.
422NoProvide more text.
429YesWait until resets_at or honor Retry-After.
500 / 502 / 503YesExponential backoff with jitter, cap at 3 retries.

The official SDKs implement the right retry strategy for every code — you don't need to think about this if you use them.

Where to go next