Bulk runs

The simplest way to fill many PDFs is the batch endpoint. Upload a spreadsheet, map the columns once, and Emboss fills a PDF per row for you. See Batch fill for that flow.

You can also fill many PDFs with a client-side loop, calling the single-form endpoints yourself. Reach for the loop when you want full control over concurrency and retries, or when your inputs are separate PDFs rather than rows of one form.

The pattern

Each form follows the usual chain — create, poll until ready, fill, download. For a batch, run several of those chains concurrently, but cap how many are in flight so you don't blow past the rate limit (the default is 60 requests per window).

async function runBatch(pdfPaths, key, concurrency = 5) {
  const queue = [...pdfPaths];
  const results = [];
  async function worker() {
    while (queue.length) {
      const path = queue.shift();
      try {
        results.push({ path, status: await fillOne(path, key) });
      } catch (err) {
        results.push({ path, error: String(err) });
      }
    }
  }
  await Promise.all(Array.from({ length: concurrency }, worker));
  return results;
}

fillOne is your single-form chain (create → poll → fill → download). Keep concurrency modest — 5 to 10 in flight is plenty and leaves rate-limit headroom.

Handling rate limits

If a request returns 429, you're over the window's limit. Wait briefly and retry that request — an exponential backoff (1s, 2s, 4s…) works well. Lowering concurrency is the simplest way to avoid hitting it in the first place.

Track what succeeded

There's no automatic idempotency across a batch, so record which inputs finished. If a run is interrupted, re-submit only the ones that didn't complete — resubmitting a form just creates a new one.

For the single-form flow each worker runs, see Fill from your data and Tracking jobs.