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.
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
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:
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
| Provider | Rate limit | Batch recommendation |
|---|---|---|
| Signbee (free) | 100/min | 10 concurrent, 1s delay |
| Signbee (paid) | 1,000/min | 50 concurrent, 500ms delay |
| DocuSign | 1,000/hour | 5 concurrent, 3.6s delay |
| HelloSign | 100/min | 10 concurrent, 1s delay |
| PandaDoc | 300/min | 20 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.