Skip to content

Errors and limits

Every error is JSON with a single message field, including upload-layer failures:

{ "error": "This account's monthly processing safety limit has been reached." }

Documented application errors under /api/v1 use this JSON shape. Clients should still handle an unreadable response defensively in case a proxy or hosting layer fails first.

Status When What to do
400 Bad upload: missing image field, unsupported or invalid bytes, or over 25 MB. Send a valid JPEG, PNG, or WebP as multipart field image.
401 Missing, malformed, unknown, or revoked API key. Check the Authorization: Bearer noai_... header; contact us for a new key if needed.
402 The key’s Pro or Lifetime plan is no longer active. Renew the plan or contact us.
429 Rate limited. Back off and retry later.
5xx Something failed on our side. Retry with backoff. If a successful response was lost in transit, repeating the request can count again.

Requests are rate-limited per IP on a rolling window shared with the free endpoints. Batch pipelines should keep a small concurrency (4 to 8 in-flight requests) and treat 429 as a signal to back off, not an error to alert on.

  • Paid marking workflows share a limit of 10,000 protection operations per account per month. Valid uploads count when processing starts.
  • Verification uses the same account-level limit of 50,000 actual image checks per month as the dashboard.
  • When either monthly limit is reached, the API returns 429 until the next monthly window.

The dashboard and connector workflows also limit browser batches to 25 images, repository scans to 10 per user per day, and opt-in private storage to 10 GB per account. A separate service-wide emergency ceiling of 250 repository scans per day may pause new scans for everyone. It is not a per-user allowance.

Marking the same bytes twice produces two marked copies and counts two images. The standardized metadata field is deterministic, while optional pixel layers may differ. If you need deduplication, check first: a file that already answers optedOut: true does not need the field added again.