Errors, quotas, and retries
Handle HTTP errors, shared account quotas, rate limits, and uncertain submissions.
Errors use a JSON envelope. Use error.code for application logic and retain the public request_id when contacting support. Messages are human-readable and may vary in language or wording.
{ "error": { "code": "INVALID_REQUEST", "message": "Scan input must contain non-empty text.", "request_id": "example-request-id" }}Quota responses additionally include a typed quota object and a usage snapshot. Branch on quota.limit_kind, not on the message. retry_after is seconds; reset_at is a Unix timestamp in milliseconds when the limit has a fixed reset. next_actions contains trusted Control Plane URLs suitable for CLI and UI conversion flows.
{ "error": { "code": "ANONYMOUS_QUOTA_EXHAUSTED", "message": "Your anonymous daily Scan Unit allowance is exhausted.", "request_id": "example-request-id" }, "quota": { "limit_kind": "anonymous_daily_scan_units", "retry_after": 3600, "reset_at": 1789084800000, "next_actions": { "sign_in_url": "https://control.patronus.studio/", "usage_url": "https://control.patronus.studio/", "upgrade_url": "https://control.patronus.studio/" } }, "usage": { "daily_scan_units": 100, "daily_limit": 100, "day_resets_at": 1789084800000 }}| HTTP | Codes you may encounter | Action |
|---|---|---|
400, 422 | INVALID_REQUEST, PDF_TEXT_LAYER_REQUIRED, INVALID_MCP_SERVER, INVALID_MULTIPART, INVALID_IDEMPOTENCY_KEY, REQUEST_TOKEN_LIMIT_EXCEEDED | Correct the input or configuration before retrying. PDF_TEXT_LAYER_REQUIRED means every PDF page requires OCR, which is not currently supported. Free/Personal inputs above 25,000 tokens must be reduced. |
401 | AUTH_REQUIRED, INVALID_API_KEY | Supply a valid, unexpired API key. |
403 | SCOPE_REQUIRED | Use a key with the required scan permission. |
409 | IDEMPOTENCY_CONFLICT, IDEMPOTENCY_IN_PROGRESS | Use the same body with the same key; wait if the original submission is still processing. |
404 | JOB_NOT_FOUND, NOT_FOUND | Check the endpoint, account, job ID, and retrieval window. |
413 | REQUEST_TOO_LARGE, SCAN_CONTENT_TOO_LARGE, FILE_TOO_LARGE, MCP_SERVER_TOO_LARGE | Reduce the input. Raw file uploads allow 10 MB. Extracted canonical content allows 100,000 bytes for Anonymous, Free, and Personal, or 1,000,000 bytes for Pro. |
415 | INVALID_REQUEST | Send JSON or multipart form data with the correct Content-Type. |
429 | RATE_LIMITED, TOKEN_RATE_LIMITED, MONTHLY_LIMIT_REACHED, ANONYMOUS_QUOTA_EXHAUSTED | Honor Retry-After when supplied or check your monthly allowance. |
502, 504 | MCP_SERVER_FETCH_FAILED | The remote MCP server could not be read. Check its availability. |
500 | INTERNAL_ERROR | Retain the request ID and investigate before resubmitting. |
503 | UPSTREAM_UNAVAILABLE, UPSTREAM_NOT_CONFIGURED, AUTH_UNAVAILABLE, API_KEYS_NOT_CONFIGURED | The service cannot complete the request. Retry later with the submission caveat below. |
504 | UPSTREAM_TIMEOUT | The service timed out; the submission may already have started. |
Quotas
Section titled “Quotas”Usage is measured in Scan Units. Every input type uses the same formula after preparation or document extraction:
| Input | Scan Units |
|---|---|
| Text | max(1, ceil(input_tokens / 1,000)) |
| MCP server metadata | max(1, ceil(input_tokens / 1,000)) |
| Documents | max(1, ceil(input_tokens / 1,000)) |
| Static webpage | max(1, ceil(input_tokens / 1,000)) |
Tokens are counted with cl100k_base after content preparation. All parts of a submission, including accompanying text, are added before rounding once. For example, 8,200 tokens cost nine Scan Units whether they came from text, documents, a webpage, or MCP metadata. request_units remains a compatibility alias for scan_units in API responses.
Token rate limits still apply to every input type, independently of its request-unit charge.
| Limit | Free | Personal | Pro |
|---|---|---|---|
| Monthly Scan Units | 30,000 | 100,000 | Unlimited |
| Maximum token balance | 50,000 | 50,000 | 1,000,000 |
| Balance refill | 1,500 tokens/s | 3,000 tokens/s | 25,000 tokens/s |
| Input tokens per submission | 25,000 | 25,000 | Bounded by the token balance and 1 MB content limit |
Free and Personal have no separate daily account quota. Monthly windows reset at UTC calendar boundaries. Pro has no monthly Scan Unit quota; its 1,000,000-token balance permits a large single scan and refills at 25,000 tokens per second.
All API keys and authenticated MCP calls for one account share the same counters and token balance. A submission needs enough remaining units and enough tokens in that balance. The balance refills continuously up to its maximum; it does not describe Ark’s processing speed.
For example, a Personal account with a full balance can immediately admit two 25,000-token submissions. A third needs roughly nine seconds of refill; on Free it needs roughly 17 seconds. Smaller submissions can fit sooner. Retry-After gives the delay for the rejected input.
Raw document uploads allow 10 MB. Canonically prepared scan content allows 100,000 bytes for Anonymous, Free, and Personal, or 1,000,000 bytes for Pro. Inputs above a size or per-submission token limit must be reduced; they are input errors, not temporary rate limits.
Polling does not consume Scan Units. It still has abuse rate limits. There are no automatic overage charges. Check the Control Plane usage page for current usage and reset times.
Retry carefully
Section titled “Retry carefully”For GET, retry transient errors with a bounded backoff. When present, Retry-After is a delay in seconds. Stop when your deadline or the job retrieval window is reached.
For POST, send an Idempotency-Key if you may need to retry:
-H 'Idempotency-Key: scan-001'Keep the key, JSON body, Content-Type, and Prefer header unchanged when retrying. For 15 minutes, the same account and key replay the original response without another scan or charge. A different body returns 409; an in-flight submission returns 409 with Retry-After: 1. Use a new key for a new scan. For multipart uploads, keep the fields and files in the same order, with the same names, types, and contents. A new multipart boundary is allowed.
Scan Units and token credit are reserved before Ark. A later upstream error does not refund them. A network timeout can mean work has already started. Without an idempotency key, resubmitting can create and charge another scan. With a key, an uncertain upstream outcome remains recorded until expiry; replaying it does not silently start another scan. Keep returned job IDs and poll them.
A job’s completion.failures[].retryable describes an analysis failure. It does not make resubmission idempotent or guarantee that a retry will succeed.
| Field | Type | Details |
|---|---|---|
error | object | Required. |
quota | QuotaError | Optional. |
usage | object | Optional. Current usage snapshot for quota and input-limit failures. |