Batch and ZIP
POST /v1/convert/batch converts many documents in one request. You can send several files, one ZIP archive, or both. The options (format, schema, mode, webhook_url) apply to every file in the batch.
Several files
Send each file in the files field, repeating the field name once per document:
curl -s https://api.docslice.app/v1/convert/batch \
-H "Authorization: Bearer $DOCSLICE_API_KEY" \
-F "files=@notes.txt" \
-F "files=@contract.txt" \
-F "format=markdown"
import json
import os
import requests
api_key = os.environ["DOCSLICE_API_KEY"]
names = ["notes.txt", "contract.txt"]
# the same field name ("files") is repeated, once per document
files = [("files", (name, open(name, "rb"), "text/plain")) for name in names]
response = requests.post(
"https://api.docslice.app/v1/convert/batch",
headers={"Authorization": f"Bearer {api_key}"},
files=files,
data={"format": "markdown"},
timeout=300,
)
print(json.dumps(response.json(), indent=2))
const fs = require("node:fs");
const form = new FormData();
for (const name of ["notes.txt", "contract.txt"]) {
// the same field name ("files") is repeated, once per document
form.append("files", new Blob([fs.readFileSync(name)], { type: "text/plain" }), name);
}
form.append("format", "markdown");
fetch("https://api.docslice.app/v1/convert/batch", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.DOCSLICE_API_KEY}` },
body: form,
})
.then((response) => response.json())
.then((data) => console.log(JSON.stringify(data, null, 2)));
<?php
// PHP's cURL cannot repeat one field name with CURLFile, so the multipart body is written by hand
$boundary = bin2hex(random_bytes(16));
$body = '';
foreach (['notes.txt', 'contract.txt'] as $name) {
$body .= "--$boundary\r\n"
. "Content-Disposition: form-data; name=\"files\"; filename=\"$name\"\r\n"
. "Content-Type: text/plain\r\n\r\n"
. file_get_contents($name) . "\r\n";
}
$body .= "--$boundary\r\nContent-Disposition: form-data; name=\"format\"\r\n\r\nmarkdown\r\n--$boundary--\r\n";
$curl = curl_init('https://api.docslice.app/v1/convert/batch');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DOCSLICE_API_KEY'),
"Content-Type: multipart/form-data; boundary=$boundary",
],
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 300,
]);
echo curl_exec($curl);
curl_close($curl);
A ZIP archive
Send a .zip and DocSlice opens it and converts the supported files inside:
curl -s https://api.docslice.app/v1/convert/batch \
-H "Authorization: Bearer $DOCSLICE_API_KEY" \
-F "files=@batch.zip" \
-F "format=markdown"
import json
import os
import requests
api_key = os.environ["DOCSLICE_API_KEY"]
with open("batch.zip", "rb") as handle:
response = requests.post(
"https://api.docslice.app/v1/convert/batch",
headers={"Authorization": f"Bearer {api_key}"},
files={"files": ("batch.zip", handle, "application/zip")},
data={"format": "markdown"},
timeout=300,
)
print(json.dumps(response.json(), indent=2))
const fs = require("node:fs");
const form = new FormData();
form.append("files", new Blob([fs.readFileSync("batch.zip")], { type: "application/zip" }), "batch.zip");
form.append("format", "markdown");
fetch("https://api.docslice.app/v1/convert/batch", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.DOCSLICE_API_KEY}` },
body: form,
})
.then((response) => response.json())
.then((data) => console.log(JSON.stringify(data, null, 2)));
<?php
$curl = curl_init('https://api.docslice.app/v1/convert/batch');
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('DOCSLICE_API_KEY')],
CURLOPT_POSTFIELDS => [
'files' => new CURLFile('batch.zip', 'application/zip', 'batch.zip'),
'format' => 'markdown',
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 300,
]);
echo curl_exec($curl);
curl_close($curl);
Hidden files (names that start with a dot) and __MACOSX entries are ignored, and so are files with an extension DocSlice does not read. A DOCX is not opened as an archive even though it is a ZIP container.
The answer
The answer reports the batch as a whole and one entry per file:
{
"success": true,
"mode": "sync",
"batch_id": "5d1c0a7e",
"total_files": 2,
"successful_files": 2,
"failed_files": 0,
"total_pages_billed": 2,
"output_format": "markdown",
"results": [
{"file_name": "notes.txt", "status": "success", "pages": 1, "ocr_applied": false, "schema_applied": false, "input_truncated": false, "data": "...", "error": null}
]
}
A failure on one file does not stop the others. That file has status: "error" and a message in error; the remaining files are converted and only successful pages are counted in total_pages_billed.
Limits
- Up to 100 files and 200 MB in total per request, and 50 MB per file. A batch over the limits is refused with
413. - The monthly quota applies to the whole batch. If it is already used up when you send the batch, the request is refused with
402. If it runs out in the middle of the batch, the files that no longer fit come back withstatus: "error"and a quota message, and the files before them keep their results. - The rate limit counts the batch as one request.
Asynchronous batches
For large batches use mode=async. You receive 202 with a batch_id, total_files and a status_url (/v1/jobs/<batch_id>); poll that job, or pass a webhook_url to receive a batch.completed or batch.failed event. See Asynchronous jobs and Webhooks.