Formats, limits, errors
Supported formats
Documents and images (text recognition, 16 formats):
.pdf · .doc · .docx · .xls · .xlsx · .ppt · .pptx · .jpg · .jpeg · .png · .tiff · .tif · .heic · .txt · .rtf · .odt
Archives and containers (recursive unpacking, depth ≤ 3; 5 formats):
.zip · .7z · .rar · .eml · .msg (Outlook emails are converted to .eml)
Files inside an archive whose extension isn’t on the list aren’t processed (they’re silently skipped during unpacking). Digital-signature files (.sig, .sign) and files without an extension aren’t processed.
Don’t see the format you need? Email hello@hotdoc.io — we’ll add it for free.
Limits
The values below are defaults; they may differ in your deployment/plan.
| Limit | Value | Scope |
|---|---|---|
| Total job size | 300 MiB (default, configurable) | checked before and after each source |
Upload size (POST /v1/jobs/upload and gRPC Upload) | 20 MiB (config grpc.maxRecvMsgBytes) | per file/message |
| Size of a single downloaded source | 20 MiB (default) | per file |
| Number of input files per job | 20 (default) | sourceUrls |
| Number of unpacked files | 20 (default) | after unpacking |
| Concurrent jobs per account | 5 (default) | in-flight |
| Converter response size | 64 MiB | per file |
| Converter timeout | 90 s | per file |
| Source download timeout | 30 s, ≤ 3 redirects | per source |
| OCR retries | 3 attempts × 10 s | per file |
| Archive unpacking depth | ≤ 3 (default) | recursive |
| Maximum prompt size | 64 KiB | exceeding it → 400 error |
| Request rate | 120 req/min per principal (burst 40), per-replica | API key or user |
Error codes
The API uses standard HTTP codes. The code in the response body is the numeric gRPC code (for example, 8 = ResourceExhausted). On REST endpoints (except POST /v1/jobs/upload), the body is {"code": <int>, "message": "…", "details": [...]}. For POST /v1/jobs/upload, it’s {"code": <int>, "message": "…"}, with no details.
| Code | When it happens |
|---|---|
400 | bad request: prompt larger than 64 KiB, unknown neural.type, empty model/apiKey, missing ocr.provider / empty ocr.model / empty ocr.providerKey, invalid reasoningEffort, neural.chunkBudgetTokens out of the 16000–2000000 range, creating a key with expiresAt in the past, or idempotency key reused with different request parameters |
401 | token missing or unrecognized (no Authorization header, or the Bearer prefix is missing) |
403 | token or key is invalid, or the action is forbidden; this includes using an expired, revoked, or unverified key/token |
404 | the job or key doesn’t belong to your account |
429 | three causes (all gRPC ResourceExhausted): (1) free-plan quota exhausted — message: "job quota exceeded" plus a QuotaExceededDetail{ jobsUsed, jobsLimit }; (2) concurrent-job limit exceeded — message: "too many concurrent jobs" (no details); (3) rate limit exceeded — on gRPC/REST the message reads "<method> is rejected by grpc_ratelimit middleware, please retry later. rate limit exceeded", and on POST /v1/jobs/upload it’s message: "rate limit exceeded" (no details). |
5xx | internal service error; retry with exponential backoff |
To tell the three 429 cases apart: quota — by the presence of QuotaExceededDetail; concurrency — by the too many concurrent jobs substring; rate — by the grpc_ratelimit / rate limit exceeded substring.
Errors within a job (a single file failed to process) aren’t returned as an HTTP code — they arrive in the job result at the file level, and the job status becomes JOB_STATUS_PARTIAL (if at least one file got a successful model answer) or JOB_STATUS_FAILED (if none did).
OCR-stage problems surface as job-level markers (not create-time HTTP errors); how the job ends depends on the problem: Auth/config markers that fail the whole job: provider_auth_failed, provider_key_required, provider_required, model_required, unsupported_provider. Per-file permanent markers (those files fail, job may end JOB_STATUS_PARTIAL): too_many_pages, unsupported_request, file_too_large, unsupported_format. Transient backend issues are retried internally and don’t surface as job failures: rate_limited, ocr_backend_overloaded, ocr_backend_error, ocr_backend_unavailable, ocr_timeout. Create-time validation errors (missing ocr.provider, empty ocr.model, or empty ocr.providerKey) → 400 before processing begins.