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_…"
}| Type | Status | What to do |
|---|---|---|
invalid-request | 400 | Read `detail`: it names the field and what it accepts. The request schema is in the API reference and in openapi.json. |
unauthorized | 401 | Send `Authorization: Bearer up_live_…`. A key with a typo is refused before any lookup by its checksum; copy it again from the dashboard. |
key-revoked | 401 | The key was revoked in the dashboard. Create a new one and replace it. |
forbidden-origin | 403 | A 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-cors | 403 | A 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-found | 404 | Check the path. Lists are allow, block and reserved, and a key reads only its own project’s lists. |
method-not-allowed | 405 | Use POST for checks and feedback, GET for usage and health, PUT and DELETE for lists. |
unsupported-media-type | 415 | Send `Content-Type: application/json`. Nothing accepts a form or a file. |
payload-too-large | 413 | Text is at most 10,000 characters, a batch at most 100 items, a list at most 5,000 terms. |
subscription-required | 402 | Live 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-exceeded | 429 | The 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-limited | 429 | Wait `Retry-After` seconds. Bursts are per key per minute by plan, and per address for publishable keys. |
test-key-daily-cap | 429 | An 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-unknown | 400 | Pin a version listed on the changelog, such as 2026-09-29, or leave `engine` out for the latest stable. |
misconfigured | 503 | A 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.