Unprofane
DocsErrors

Errors

Every failure is a problem-details body with a stable type URI that points at one of the pages below.

HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json

{
  "type": "https://unprofane.com/docs/errors/unauthorized",
  "title": "No valid API key.",
  "status": 401,
  "detail": "Send your API key as `Authorization: Bearer up_live_…`.",
  "instance": "req_…"
}
TypeStatusWhat to do
invalid-request400Read `detail`: it names the field and what it accepts. The request schema is in the API reference and in openapi.json.
unauthorized401Send `Authorization: Bearer up_live_…`. A key with a typo is refused before any lookup by its checksum; copy it again from the dashboard.
key-revoked401The key was revoked in the dashboard. Create a new one and replace it.
forbidden-origin403A publishable key is limited to the origins on its project. Add the origin in the dashboard, or use a secret key from your server. Publishable keys may only call /v1/check.
forbidden-secret-cors403A secret key in a browser is visible to every visitor. Use a publishable key (up_pk_…) with an origin allowlist, or call from your server.
not-found404Check the path. Lists are allow, block and reserved, and a key reads only its own project’s lists.
method-not-allowed405Use POST for checks and feedback, GET for usage and health, PUT and DELETE for lists.
unsupported-media-type415Send `Content-Type: application/json`. Nothing accepts a form or a file.
payload-too-large413Text is at most 10,000 characters, a batch at most 100 items, a list at most 5,000 terms.
subscription-required402Live and publishable keys answer only on a plan. An account with no plan (a new one, or one whose plan was cancelled, went unpaid past the grace or whose research grant ended) has test keys only, 100 checks a day. Choose a plan on the pricing page or the dashboard’s billing page, from $9 a month; the same keys answer within a minute of the merchant’s confirmation. Until then, build with a test key.
quota-exceeded429The Research plan stops at its monthly allowance, and a paid plan stops at the spend cap its owner set. Raise the cap or choose a plan in the dashboard, or wait for the period to reset. A paid plan with no spend cap is never stopped.
rate-limited429Wait `Retry-After` seconds. Bursts are per key per minute by plan, and per address for publishable keys.
test-key-daily-cap429An account’s test keys share one cap of 100 checks a day, on every plan and with none. Use a live key on a plan, or wait for tomorrow (UTC).
engine-unknown400Pin a version listed on the changelog, such as 2026-09-29, or leave `engine` out for the latest stable.
misconfigured503A server setting is missing on our side. Retry shortly; we are alerted.

Retrying

Retry a 429 after Retry-After seconds and a 503 after a short backoff with jitter. Never retry a 400 or a 401 unchanged. A model failure is never a 5xx: the check answers with the rules result and engine.degraded: true.

When the API cannot be reached

A check waits at most 800 ms for the model, then answers with the rules. Set your client timeout a few seconds above that, and decide for each place text appears what a timeout means:

  • Hold it where a text stays up once shown: usernames, profile fields, product text. Keep it unpublished and check it again.
  • Let it through where a moment of exposure costs less than a lost message: chat, live comments. Check it again in the background and take it down if the answer is block.
  • Check in the browser with a publishable key as a first pass where the answer must come while the person types; it runs the rules only, so check the final text from your server.