Skip to content
Patronus
Website

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
}
}
HTTPCodes you may encounterAction
400, 422INVALID_REQUEST, PDF_TEXT_LAYER_REQUIRED, INVALID_MCP_SERVER, INVALID_MULTIPART, INVALID_IDEMPOTENCY_KEY, REQUEST_TOKEN_LIMIT_EXCEEDEDCorrect 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.
401AUTH_REQUIRED, INVALID_API_KEYSupply a valid, unexpired API key.
403SCOPE_REQUIREDUse a key with the required scan permission.
409IDEMPOTENCY_CONFLICT, IDEMPOTENCY_IN_PROGRESSUse the same body with the same key; wait if the original submission is still processing.
404JOB_NOT_FOUND, NOT_FOUNDCheck the endpoint, account, job ID, and retrieval window.
413REQUEST_TOO_LARGE, SCAN_CONTENT_TOO_LARGE, FILE_TOO_LARGE, MCP_SERVER_TOO_LARGEReduce 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.
415INVALID_REQUESTSend JSON or multipart form data with the correct Content-Type.
429RATE_LIMITED, TOKEN_RATE_LIMITED, MONTHLY_LIMIT_REACHED, ANONYMOUS_QUOTA_EXHAUSTEDHonor Retry-After when supplied or check your monthly allowance.
502, 504MCP_SERVER_FETCH_FAILEDThe remote MCP server could not be read. Check its availability.
500INTERNAL_ERRORRetain the request ID and investigate before resubmitting.
503UPSTREAM_UNAVAILABLE, UPSTREAM_NOT_CONFIGURED, AUTH_UNAVAILABLE, API_KEYS_NOT_CONFIGUREDThe service cannot complete the request. Retry later with the submission caveat below.
504UPSTREAM_TIMEOUTThe service timed out; the submission may already have started.

Usage is measured in Scan Units. Every input type uses the same formula after preparation or document extraction:

InputScan Units
Textmax(1, ceil(input_tokens / 1,000))
MCP server metadatamax(1, ceil(input_tokens / 1,000))
Documentsmax(1, ceil(input_tokens / 1,000))
Static webpagemax(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.

LimitFreePersonalPro
Monthly Scan Units30,000100,000Unlimited
Maximum token balance50,00050,0001,000,000
Balance refill1,500 tokens/s3,000 tokens/s25,000 tokens/s
Input tokens per submission25,00025,000Bounded 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.

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:

Terminal window
-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.

FieldTypeDetails
errorobjectRequired.
quotaQuotaErrorOptional.
usageobjectOptional. Current usage snapshot for quota and input-limit failures.