Endpoint
Parse Batch
POST /v1/parse/batch accepts up to 10 PDFs in one request, applies the same parse options to all of them, and reports per-file status and artifacts under a single batch_id.
Limits
10 files / 100 MB / 500 pages
Default v1 caps, plus 2 batch submits per minute.
Idempotency
Required
One-hour window from submit, shorter than the Stripe-style 24-hour convention.
Artifact TTL
1 hour
Measured from terminal completion. Expired downloads return 410 output_expired.
Model
How Batches Behave
- Preflight is all-or-nothing: one invalid file rejects the whole submit before anything is queued.
- Parse options are shared across the batch. v1 has no per-file settings.
- Polling is the source of truth. Webhooks are best-effort terminal notifications with bounded retry.
- Terminal statuses are
completed,completed_with_failures, andfailed. No per-file retry in v1. estimated_creditsis an estimate only:max(10, selected_pages)per file. v1 does not settle credits on this lane.- Every status and download request is owner-scoped. Unknown batches or owner mismatches return
404.
Endpoint
Batch Endpoint
POST/v1/parse/batch
Submit up to 10 PDFs as one async parse batch with shared parse options and per-file artifacts.
Auth
Bearer token required on submit, status, and artifact download requests.
Idempotency
Required Idempotency-Key. The default idempotency window is one hour from submit, intentionally shorter than Stripe-style 24-hour idempotency. Reusing the same key and request replays the accepted response; reusing the key with a different request returns 409 idempotency_key_reused.
Content Type
multipart/form-data
Headers
| Name | Type | Required | Location | Description |
|---|---|---|---|---|
| Authorization | Bearer <API_KEY> | Yes | header | User-owned API key created in the DocuShell dashboard. |
| Idempotency-Key | string | Yes | header | Required for every batch submit. Use a fresh key per logical batch and reuse it only for the exact same retry. |
Request Fields
| Name | Type | Required | Location | Description |
|---|---|---|---|---|
| files[] | file[] | Yes | multipart | PDF uploads for the batch. Every file is preflighted before enqueue; one invalid file rejects the whole submit. |
| page_range | string | No | multipart | Shared page selector applied to every file, such as 1-3,5. Selected pages count toward the batch total page limit. |
| include_header_footer | boolean | No | multipart | Shared setting. Set to true to keep repeated headers and footers in extracted output.Default: false |
| use_struct_tree | boolean | No | multipart | Shared setting. Set to true to prefer native tagged-PDF structure when available.Default: false |
| sanitize | boolean | No | multipart | Shared setting. Set to true to mask email addresses, URLs, and phone numbers in extracted output.Default: false |
| reading_order | `xycut` | `off` | No | multipart | Shared reading-order strategy. |
| table_method | `default` | `cluster` | No | multipart | Shared table-detection strategy. |
| keep_line_breaks | boolean | No | multipart | Shared setting. Set to true when text-oriented output should preserve original line breaks more closely.Default: false |
| output_mode | `json` | `both` | `html` | `all` | No | multipart | Backward-compatible artifact bundle selector. Do not send this together with formats.Default: both |
| formats | `json` | `markdown` | `html` | `text` | `annotated_pdf` | `markdown_with_html` | `markdown_with_images` | `tagged_pdf` | No | multipart | Explicit artifact list. Send repeated fields or a comma-separated value. Do not send this together with output_mode. |
| hybrid_mode | `auto` | `full` | No | multipart | Optional shared hybrid triage override when the hybrid backend is enabled by operations. |
| image_output | `off` | `embedded` | `external` | No | multipart | Shared image handling for image-capable outputs. |
| x-docushell-webhook-url | string | No | header | Optional public HTTPS endpoint for the terminal batch webhook. URLs with credentials, localhost, private, reserved, or metadata-service addresses are rejected. |
| x-docushell-webhook-secret | string | No | header | Required when x-docushell-webhook-url is present. Must be 16-256 characters with sufficient variety; do not reuse an API key. |
| x-docushell-webhook-endpoint-id | string | No | header | Saved managed webhook endpoint id. Use this instead of sending a per-request webhook URL and secret. |
Request Notes
- Batch parse is async-only. Submit returns
202; pollGET /v1/parse/batch/:batchIdfor truth. - All files are preflighted before enqueue. Empty, non-PDF, corrupt, password-protected, oversized, invalid-page-range, per-file page-limit, total-byte-limit, and total-page-limit failures reject the whole submit.
- Default v1 limits are 10 files, 100 MB total upload, 500 selected pages, and 2 batch submits per minute.
- Parse options are shared across the batch. v1 does not support per-file parse settings.
- Send either
output_modeorformats, not both. Only one markdown-style format (markdown,markdown_with_html, ormarkdown_with_images) can be requested. - The batch lane has separate backpressure. Queue saturation returns
503 server_busywithRetry-After; rate limits return429. - Batch responses report
estimated_creditsonly: each file estimatesmax(10, selected_pages)credits. v1 does not perform final credit settlement on this lane. - Every status and download request is owner-scoped. Unknown batches, files, or owner mismatches return
404. - Artifacts expire one hour after terminal completion by default. Batch idempotency expires one hour after submit by default.
- Terminal statuses are
completed,completed_with_failures, andfailed. No per-file retry is attempted in v1. - Webhooks are signed best-effort terminal notifications with short bounded retry. Polling remains the source of truth.
Multipart batch submit
bash
curl -X POST "https://api.docushell.com/api/v1/parse/batch" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Idempotency-Key: parse-batch-demo-001" \
-H "x-docushell-webhook-url: https://example.com/docushell/webhooks" \
-H "x-docushell-webhook-secret: replace_with_a_long_random_secret" \
-F "files[]=@./report-q1.pdf;type=application/pdf" \
-F "files[]=@./report-q2.pdf;type=application/pdf" \
-F "page_range=1-5" \
-F "formats=json,markdown"Try It Now
Console placeholder for safe sandbox execution.
Accepted batch response
json
{
"batch_id": "9c7f2f2e-4f4b-4cbf-bb12-7fd3c1f4f2ab",
"service": "parse-pdf",
"status": "queued",
"counts": {
"total": 2,
"queued": 2,
"processing": 0,
"completed": 0,
"failed": 0
},
"usage": {
"total_upload_bytes": 1843200,
"total_selected_pages": 12
},
"estimated_credits": 20,
"created_at": "2026-05-23T10:12:30.000Z",
"updated_at": "2026-05-23T10:12:30.000Z",
"completed_at": null,
"expires_at": null,
"webhook_delivery": {
"status": "pending"
},
"metrics": null,
"files": [
{
"file_id": "file_6c27f6d4-43c1-48c6-a3c8-3a89d0a3cf20",
"status": "queued",
"page_count": 8,
"billable_pages": 8,
"estimated_credits": 10
},
{
"file_id": "file_95ac2fb4-060d-427c-9f86-864747dfb935",
"status": "queued",
"page_count": 4,
"billable_pages": 4,
"estimated_credits": 10
}
],
"links": {
"status": "/v1/parse/batch/9c7f2f2e-4f4b-4cbf-bb12-7fd3c1f4f2ab",
"download": "/v1/parse/batch/9c7f2f2e-4f4b-4cbf-bb12-7fd3c1f4f2ab/download"
}
}Batch status response
json
{
"batch_id": "9c7f2f2e-4f4b-4cbf-bb12-7fd3c1f4f2ab",
"service": "parse-pdf",
"status": "completed_with_failures",
"counts": {
"total": 2,
"queued": 0,
"processing": 0,
"completed": 1,
"failed": 1
},
"usage": {
"total_upload_bytes": 1843200,
"total_selected_pages": 12
},
"estimated_credits": 20,
"created_at": "2026-05-23T10:12:30.000Z",
"updated_at": "2026-05-23T10:14:04.000Z",
"completed_at": "2026-05-23T10:14:04.000Z",
"expires_at": "2026-05-23T11:14:04.000Z",
"webhook_delivery": {
"status": "delivered"
},
"metrics": {
"queue_wait_ms": 214,
"duration_ms": 82341
},
"files": [
{
"file_id": "file_6c27f6d4-43c1-48c6-a3c8-3a89d0a3cf20",
"status": "completed",
"page_count": 8,
"billable_pages": 8,
"estimated_credits": 10,
"artifacts": {
"json_download": "/v1/parse/batch/9c7f2f2e-4f4b-4cbf-bb12-7fd3c1f4f2ab/files/file_6c27f6d4-43c1-48c6-a3c8-3a89d0a3cf20/download?format=json",
"markdown_download": "/v1/parse/batch/9c7f2f2e-4f4b-4cbf-bb12-7fd3c1f4f2ab/files/file_6c27f6d4-43c1-48c6-a3c8-3a89d0a3cf20/download?format=markdown"
}
},
{
"file_id": "file_95ac2fb4-060d-427c-9f86-864747dfb935",
"status": "failed",
"page_count": 4,
"billable_pages": 4,
"estimated_credits": 10,
"failure_code": "corrupt_pdf"
}
],
"links": {
"status": "/v1/parse/batch/9c7f2f2e-4f4b-4cbf-bb12-7fd3c1f4f2ab",
"download": "/v1/parse/batch/9c7f2f2e-4f4b-4cbf-bb12-7fd3c1f4f2ab/download"
}
}Batch ZIP download
bash
curl "https://api.docushell.com/api/v1/parse/batch/9c7f2f2e-4f4b-4cbf-bb12-7fd3c1f4f2ab/download" \
-H "Authorization: Bearer YOUR_API_KEY" \
--output parse-batch.zipPer-file artifact download
bash
curl "https://api.docushell.com/api/v1/parse/batch/9c7f2f2e-4f4b-4cbf-bb12-7fd3c1f4f2ab/files/file_6c27f6d4-43c1-48c6-a3c8-3a89d0a3cf20/download?format=json" \
-H "Authorization: Bearer YOUR_API_KEY" \
--output report.jsonArtifacts
- Per-file download links appear under
files[].artifactsafter each completed file is promoted. - The batch ZIP is generated on demand from completed file artifacts, streamed, and removed after the response finishes.
- Per-file artifact downloads can be retried until the batch expires.
- The per-file
formatquery must match an artifact requested for the batch. - Original filenames are not part of the public status payload or webhook logs.
Poll And Download
- Poll
GET /v1/parse/batch/:batchIduntilstatusbecomescompleted,completed_with_failures, orfailed. - When the batch completes or partially completes, use
GET /v1/parse/batch/:batchId/downloadfor the generated ZIP or use eachfiles[].artifacts.*_downloadlink for a specific file artifact. - If a file status is
failed, its per-file download returns409 batch_file_failed. Continue downloading completed files untilexpires_at.
Failure Notes
invalid_pdf,corrupt_pdf,password_protected,invalid_page_range, andpage_limit_exceededcan be returned during preflight before a batch is accepted.server_busywithRetry-Aftermeans the dedicated batch queue or active batch lane is saturated. Retry later with the same Idempotency-Key only for the exact same request.- Download
400means the requested format was not requested for this batch. - Download
425 batch_not_readymeans the batch or file is not terminal yet. - Download
409 batch_file_failedmeans that accepted file reached a terminal failed state. - Download
410 output_expiredmeans the artifact TTL has passed.
Batch not ready
425batch_not_readyReturned when a batch or file download is attempted before terminal status.
425 error
json
{
"error": {
"code": "batch_not_ready",
"message": "Batch is not ready.",
"type": "invalid_request_error",
"request_id": "req_01JX8Y62XCDNZ2BM7TBM2M9Q8E"
}
}Batch file failed
409batch_file_failedReturned when a per-file artifact is requested for a file that failed after enqueue.
409 error
json
{
"error": {
"code": "batch_file_failed",
"message": "Batch file failed.",
"type": "invalid_request_error",
"request_id": "req_01JX8Y62XCDNZ2BM7TBM2M9Q8E"
}
}Expired output
410output_expiredReturned after the one-hour default TTL for completed batch artifacts passes.
410 error
json
{
"error": {
"code": "output_expired",
"message": "Batch output expired.",
"type": "invalid_request_error",
"request_id": "req_01JX8Y62XCDNZ2BM7TBM2M9Q8E"
}
}Queue saturated
503server_busyReturned when batch-specific queue backpressure rejects the submit.
503 error
json
{
"error": {
"code": "server_busy",
"message": "The parse batch queue is busy. Retry later.",
"type": "internal_error",
"request_id": "req_01JX8Y62XCDNZ2BM7TBM2M9Q8E"
}
}