May 6, 2026 · Developer Guide

Batch E-Signature API: How to Send Multiple Documents in One Workflow

You have 200 NDAs to send. Or 50 offer letters. Or a monthly invoice run. Here's how to batch-send documents for e-signature without hitting rate limits or losing track of failures.

Michael Beckett
Michael Beckett

Founder, Signbee

TL;DR

Most e-signature APIs process one document per call. To send in bulk, use Promise.allSettled with chunked concurrency (10-20 at a time), exponential backoff for failures, and webhook handlers to track completion. At Signbee's rate of <2s per call, 500 documents finish in under 2 minutes.

Why batch signing matters

Batch signing isn't a niche use case — it's the default for any company beyond seed stage. HR departments send offer letters in waves. Legal teams distribute NDAs to entire vendor lists. Finance runs monthly invoice signing cycles. If you're sending more than 10 documents a week, you need a batch workflow.

The problem: most e-signature APIs don't have a native batch endpoint. You call the API once per document. This means you need to handle concurrency, rate limits, and partial failures yourself. Here's the pattern that works.

The batch sending pattern

JavaScript — batch send with chunked concurrency
const BATCH_SIZE = 10; // concurrent requests per chunk
const DELAY_MS = 1000; // pause between chunks

async function sendDocument(doc) {
  const res = await fetch("https://signb.ee/api/v1/send", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": "Bearer YOUR_API_KEY",
    },
    body: JSON.stringify({
      markdown: doc.content,
      recipient_name: doc.recipientName,
      recipient_email: doc.recipientEmail,
    }),
  });
  if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
  return res.json();
}

async function batchSend(documents) {
  const results = [];

  for (let i = 0; i < documents.length; i += BATCH_SIZE) {
    const chunk = documents.slice(i, i + BATCH_SIZE);
    const chunkResults = await Promise.allSettled(
      chunk.map((doc) => sendDocument(doc))
    );
    results.push(...chunkResults);

    // Rate limit pause between chunks
    if (i + BATCH_SIZE < documents.length) {
      await new Promise((r) => setTimeout(r, DELAY_MS));
    }
  }

  const sent = results.filter((r) => r.status === "fulfilled");
  const failed = results.filter((r) => r.status === "rejected");
  console.log(`Sent: ${sent.length}, Failed: ${failed.length}`);
  return { sent, failed };
}

Error handling and retries

The key insight: use Promise.allSettled, not Promise.all. With Promise.all, a single 429 (rate limit) or 500 (server error) aborts the entire batch. Promise.allSettled completes every request and reports which succeeded and which failed.

For failed documents, implement exponential backoff:

Retry logic with exponential backoff
async function retryFailed(failedDocs, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
    await new Promise((r) => setTimeout(r, delay));

    const retryResults = await Promise.allSettled(
      failedDocs.map((doc) => sendDocument(doc))
    );

    failedDocs = retryResults
      .filter((r) => r.status === "rejected")
      .map((_, i) => failedDocs[i]);

    if (failedDocs.length === 0) break;
  }

  if (failedDocs.length > 0) {
    // Dead letter queue — alert your team
    await alertTeam(failedDocs);
  }
}

Rate limits by provider

ProviderRate limitBatch recommendation
Signbee (free)100/min10 concurrent, 1s delay
Signbee (paid)1,000/min50 concurrent, 500ms delay
DocuSign1,000/hour5 concurrent, 3.6s delay
HelloSign100/min10 concurrent, 1s delay
PandaDoc300/min20 concurrent, 500ms delay

For a full comparison of integration complexity, pricing, and developer experience, see our comparison of the 6 best e-signature APIs.

Tracking batch completion

After sending a batch, you need to know when each document is signed. Two approaches:

  • Webhooks (recommended) — Register a webhook endpoint and get notified the instant each document is signed. Zero polling overhead.
  • Polling — Check GET /api/v1/documents/{id} on a schedule. Works for small batches but doesn't scale.

Frequently Asked Questions

Can I send multiple documents in one API call?

Most APIs process one document per call. Use Promise.allSettled with chunked concurrency to send in bulk. 500 documents finish in under 2 minutes.

What are the rate limits?

Signbee: 100/min (free), 1,000/min (paid). DocuSign: 1,000/hour. Chunk your requests and add delays between batches.

How do I handle batch failures?

Use Promise.allSettled instead of Promise.all. Retry failed documents with exponential backoff (2s, 4s, 8s). Alert your team after 3 retries.

Batch-send documents for e-signature — 5 free docs/month.

Last updated: May 6, 2026 · Michael Beckett is the founder of Signbee and B2bee Ltd.

Related resources