Skip to content
Foldbun StackDocs
中Get an API keyComing soon

Layout analysis of many document pages

POST
/v1/batch/documents/parse
curl --request POST \
--url https://api.stack.foldbun.com/v1/batch/documents/parse \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "model": "paddleocr-vl", "items": [ { "image": { "id": "img_3b1f0c5e9a7d42c8b6e1f0a9d8c7b6a5f4e3d2c1b0a998877665544332211000" } } ] }'

Model paddleocr-vl (default). Items are POST /v1/documents/parse bodies without model. Semantics, modes, limits and the event format are described on the BatchRequest, BatchItem and BatchSummary schemas and in the Overview. Server-Sent Events are recommended; JSON mode is not.

Idempotency-Key
string
>= 1 characters <= 128 characters /^[\x21-\x7e]+$/

1 to 128 printable ASCII characters. Within an account, a key is remembered for 24 hours: repeating the request with the same body returns the original response (header Idempotent-Replayed: true) without charging or running the call again. The same key with a different body is 409 idempotency_conflict. See the Overview.

The model, the items and an optional time limit.

Media typeapplication/json

A batch of single calls to one endpoint. model is set once (omit it where the endpoint has a default model); an item must not contain model. Every item is validated before anything runs: a structural error is 400 invalid_request whose message names items[i], and nothing is billed. Images in items can only be {"id": "img_..."} handles. Items run strictly one after another, so the order of the array is the order of execution. At most 1,000 items and 4 MiB per request. Idempotency-Key makes item i use the derived key "b <key> <i>": repeating the same batch replays finished items (replayed: true, no charge), runs the others, and an item whose content changed fails with idempotency_conflict.

Limits: event-stream mode runs for at most 600 s, JSON mode for at most 90 s (JSON mode is not recommended beyond a few items). timeoutSec can only shorten them; a larger value is clamped without an error. When the limit passes, no new item starts, the running item is cancelled (not billed) and every item not finished is expired; the results of finished items are kept. A client disconnect stops the batch the same way. At most 1 to 4 batch requests can be open per account at once, depending on the tier (429 too_many_batches).

object
model

Model id for all items. Optional where the endpoint has a default model.

string
items
required

Single-call request bodies of the endpoint, without model, in execution order.

Array<object>
>= 1 items <= 1000 items

The request body of the single-call endpoint without model; images only as {"id": "img_..."}.

object
timeoutSec

Time limit for the whole batch in seconds, counted from the arrival of the request. Can only shorten the mode limit (600 s event stream, 90 s JSON).

number
> 0

Example

{
"model": "paddleocr-vl",
"items": [
{
"image": {
"id": "img_3b1f0c5e9a7d42c8b6e1f0a9d8c7b6a5f4e3d2c1b0a998877665544332211000"
}
}
]
}

The batch ran (items may still have failed individually: the request itself only fails before execution, with the normal error envelope). With Accept: text/event-stream the body is a stream of events, flushed one by one: progress first, then item per finished item, summary last. Otherwise it is one JSON document, or one CBOR document with Accept: application/cbor.

Events in the form event: <name> / data: <one-line JSON>, separated by a blank line. Names: progress (BatchProgress), item (BatchItem), summary (BatchSummary, last).

string

Example

event: progress
data: {"done":0,"remaining":2}
event: item
data: {"index":0,"status":"ok","result":{"id":"call_0193...","model":"dinov3","cost":0.05,"balance":9.95}}
event: progress
data: {"done":1,"remaining":1,"etaWaitMs":850}
event: item
data: {"index":1,"status":"error","error":{"code":"insufficient_balance","message":"insufficient balance: this call can cost up to 0.05 credits"}}
event: summary
data: {"counts":{"ok":1,"error":1,"expired":0},"cost":0.05,"balance":0.01,"elapsedMs":2310}
X-Request-Id
string
/^[0-9a-f]{16}$/

Server-generated request id (16 hexadecimal characters); present on every response. Quote it when contacting support.

The error envelope. The HTTP status follows the code; only retryable: true errors are worth repeating unchanged. A failed call costs 0.

Status Codes
400 invalid_request, invalid_image (the image passed the upload checks but could not be decoded), unsupported
401 unauthorized
402 insufficient_balance
403 model_locked
404 not_found, image_not_found
409 idempotency_conflict, request_in_progress
413 payload_too_large
429 rate_limited, daily_limit_reached, too_many_batches (all with Retry-After)
500 internal_error
502 upstream_error, bad_model_output
503 model_unavailable, model_busy, unavailable (all with Retry-After)
504 upstream_timeout

model_unavailable also covers a short loss of contact with the only GPU worker serving the model: for about a minute after the loss, interactive calls answer it at once with a short Retry-After (5 s), while sync and batch calls stay queued until their own deadline instead of failing.

Meaning and retry guidance for each code: Errors.

Media typeapplication/json

Body of every error response.

object
error
required

The error.

object
code
required

Stable machine-readable error code. Branch on this, not on message. Listed with the HTTP statuses under Responses.

string
message
required

Human-readable explanation. The wording can change.

string
retryable
required

True when repeating the same request unchanged can succeed.

boolean
retryAfterMs
required

Suggested wait before a retry in milliseconds; 0 when there is no hint.

integer

Example

{
"error": {
"code": "model_busy",
"message": "model sam3 is busy; retry shortly",
"retryable": true,
"retryAfterMs": 5000
}
}
Retry-After
integer
>= 1

Whole seconds to wait before retrying; present on 429 and 503.

X-Request-Id
string
/^[0-9a-f]{16}$/

Server-generated request id (16 hexadecimal characters); present on every response. Quote it when contacting support.