Guides

Batch Processing

The batch endpoint lets you submit multiple PDF URLs in a single API call. All conversions are processed asynchronously, and you can poll a single URL to monitor progress.

How it works

  1. Send a POST to /v1/convert/batch with an array of PDF URLs.
  2. The API returns a batchId and a pollUrl (status 202).
  3. Poll GET /v1/batch/:batchId to check progress.
  4. When all requests are complete, download each resultUrl.

Each URL in the batch consumes one request from your monthly quota.

Submit a batch

curl - batch request
curl -X POST https://api.pdfpipe.dev/v1/convert/batch \
  -H "Authorization: Bearer pk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "urls": [
      { "url": "https://example.com/invoice-1.pdf" },
      { "url": "https://example.com/invoice-2.pdf", "format": "text" },
      {
        "url": "https://example.com/report.pdf",
        "type": "attachment",
        "pages": "1-3",
        "metadata": { "department": "finance" }
      }
    ],
    "defaults": { "format": "json" },
    "returnMethod": "inline",
    "webhook": { "url": "https://example.com/hooks/batch-complete" }
  }'
Per-URL options: Each URL object can set format, type, pages, and metadata (string key/value pairs stored with that request). The defaults object (format, type) applies to any URL that doesn't specify its own value. returnMethod and webhook are set once at the top level and apply to the whole batch.

Batch response (202)

202 - Response
{
  "batchId": "batch_a1b2c3d4e5f6...",
  "requests": [
    { "requestId": "req_aaa...", "url": "https://example.com/invoice-1.pdf", "status": "queued" },
    { "requestId": "req_bbb...", "url": "https://example.com/invoice-2.pdf", "status": "queued" },
    { "requestId": "req_ccc...", "url": "https://example.com/report.pdf", "status": "queued" }
  ],
  "pollUrl": "/v1/batch/batch_a1b2c3d4e5f6..."
}

Poll for progress

curl - poll batch status
curl https://api.pdfpipe.dev/v1/batch/batch_a1b2c3d4e5f6... \
  -H "Authorization: Bearer pk_..."
200 - Batch status
{
  "batchId": "batch_a1b2c3d4e5f6...",
  "total": 3,
  "completed": 2,
  "failed": 0,
  "pending": 1,
  "requests": [
    { "requestId": "req_aaa...", "status": "complete", "resultUrl": "https://..." },
    { "requestId": "req_bbb...", "status": "complete", "resultUrl": "https://..." },
    { "requestId": "req_ccc...", "status": "processing" }
  ]
}
200 - Batch status (returnMethod: inline, with a failure)
{
  "batchId": "batch_a1b2c3d4e5f6...",
  "total": 2,
  "completed": 1,
  "failed": 1,
  "pending": 0,
  "requests": [
    {
      "requestId": "req_aaa...",
      "status": "complete",
      "contentType": "application/json",
      "content": "{\"pages\":[{\"pageNumber\":1,\"text\":\"...\"}],...}"
    },
    { "requestId": "req_bbb...", "status": "failed", "errorCode": "NOT_A_PDF" }
  ]
}

Poll every 5-10 seconds. When pending reaches 0, all jobs are done. Each completed request includes a presigned resultUrl, or content when the batch used returnMethod: "inline" (you can also append ?returnMethod=inline to the poll URL). Failed requests carry an errorCode; poll GET /v1/status/:requestId for the full message and suggestion.

Full Node.js example

Node.js
const response = await fetch("https://api.pdfpipe.dev/v1/convert/batch", {
  method: "POST",
  headers: {
    "Authorization": "Bearer pk_...",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    urls: pdfUrls.map(url => ({ url })),
    defaults: { format: "json" },
    webhook: { url: "https://example.com/hooks/batch" },
  }),
});

const { batchId, pollUrl } = await response.json();

// Poll until all complete
let batch;
do {
  await new Promise(r => setTimeout(r, 5000));
  // pollUrl is "/v1/batch/..." so prepend only the host
  const poll = await fetch(`https://api.pdfpipe.dev${pollUrl}`, {
    headers: { "Authorization": "Bearer pk_..." },
  });
  batch = await poll.json();
  console.log(`Progress: ${batch.completed}/${batch.total}`);
} while (batch.pending > 0);

// Download all results
for (const req of batch.requests) {
  if (req.status === "complete" && req.resultUrl) {
    const doc = await fetch(req.resultUrl).then(r => r.json());
    console.log(`${req.requestId}: ${doc.totalPages} pages`, doc.pages[0].text);
  } else if (req.status === "failed") {
    console.warn(`${req.requestId} failed: ${req.errorCode}`);
  }
}

Batch size limits

The maximum number of URLs per batch depends on your tier:

TierMax batch size
Free5 URLs
Starter25 URLs
Pro50 URLs
Business100 URLs