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:

Convert several files in one request
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:

Convert a ZIP archive
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 with status: "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.