| name | documenso-rate-limits |
| description | Implement Documenso rate limiting, backoff, and request throttling patterns.
Use when handling rate limit errors, implementing retry logic,
or optimizing API request throughput for Documenso.
Trigger with phrases like "documenso rate limit", "documenso throttling",
"documenso 429", "documenso retry", "documenso backoff".
|
| allowed-tools | Read, Write, Edit |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Documenso Rate Limits
Overview
Handle Documenso API rate limits gracefully with exponential backoff, request queuing, and fair use compliance.
Prerequisites
- Documenso SDK installed
- Understanding of async/await patterns
- Queue library (optional but recommended)
Documenso Fair Use Policy
Documenso implements fair use rate limiting. While specific limits are not publicly documented, follow these guidelines:
| Recommendation | Limit | Notes |
|---|
| Requests per second | 5-10 | Stay well under burst |
| Requests per minute | 100-200 | Sustained rate |
| Bulk operations | Use batching | Batch create recipients/fields |
| File uploads | 1 at a time | Sequential uploads |
| Polling | 5-10 second intervals | Don't poll aggressively |
Instructions
Step 1: Implement Exponential Backoff with Jitter
interface BackoffConfig {
maxRetries: number;
baseDelayMs: number;
maxDelayMs: number;
jitterMs: number;
}
const DEFAULT_BACKOFF: BackoffConfig = {
maxRetries: 5,
baseDelayMs: 1000,
maxDelayMs: 32000,
jitterMs: 500,
};
async function withExponentialBackoff<T>(
operation: () => Promise<T>,
config: Partial<BackoffConfig> = {}
): Promise<T> {
const { maxRetries, baseDelayMs, maxDelayMs, jitterMs } = {
...DEFAULT_BACKOFF,
...config,
};
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error: any) {
if (attempt === maxRetries) throw error;
const status = error.statusCode ?? error.status;
if (status !== && (status < || status >= )) {
error;
}
retryAfter = error.?.[];
: ;
(retryAfter) {
delay = (retryAfter) * ;
} {
exponentialDelay = baseDelayMs * .(, attempt);
jitter = .() * jitterMs;
delay = .(exponentialDelay + jitter, maxDelayMs);
}
.(
+
);
( (r, delay));
}
}
();
}
Step 2: Request Queue for Controlled Throughput
import PQueue from "p-queue";
const documensoQueue = new PQueue({
concurrency: 3,
interval: 1000,
intervalCap: 5,
});
async function queuedDocumensoRequest<T>(
operation: () => Promise<T>
): Promise<T> {
return documensoQueue.add(() => withExponentialBackoff(operation));
}
const results = await Promise.all(
documents.map((doc) =>
queuedDocumensoRequest(() =>
client.documents.createV0({ title: doc.title })
)
)
);
Step 3: Batch Operations
async function addRecipientsInBatch(
documentId: string,
recipients: Array<{ email: string; name: string; role: string }>
): Promise<string[]> {
const result = await queuedDocumensoRequest(() =>
client.documentsRecipients.createManyV0({
documentId,
recipients: recipients.map((r) => ({
email: r.email,
name: r.name,
role: r.role as any,
})),
})
);
return result.recipientIds ?? [];
}
async function addFieldsInBatch(
documentId: string,
fields: Array<{
recipientId: string;
type: string;
page: number;
x: number;
y: number;
}>
): <> {
(
client..({
documentId,
: fields.( ({
: f.,
: f. ,
: f.,
: f.,
: f.,
: ,
: ,
})),
})
);
}
Step 4: Rate Limit Monitor
class RateLimitMonitor {
private requestCount = 0;
private windowStart = Date.now();
private readonly windowMs = 60000;
private readonly maxRequests = 100;
async canMakeRequest(): Promise<boolean> {
const now = Date.now();
if (now - this.windowStart > this.windowMs) {
this.requestCount = 0;
this.windowStart = now;
}
if (this.requestCount >= this.maxRequests) {
const waitTime = this.windowMs - (now - this.windowStart);
console.log(`Rate limit reached. Wait ${waitTime}ms`);
return ;
}
.++;
;
}
(): { : ; : ; : } {
now = .();
{
: .,
: .(, . - .),
: .(, . - (now - .)),
};
}
}
rateLimitMonitor = ();
monitoredRequest<T>(: <T>): <T> {
(!( rateLimitMonitor.())) {
( (r, ));
}
();
}
Step 5: Idempotent Requests
import crypto from "crypto";
function generateIdempotencyKey(
operation: string,
params: Record<string, any>
): string {
const data = JSON.stringify({ operation, params });
return crypto.createHash("sha256").update(data).digest("hex");
}
const processedRequests = new Map<string, any>();
async function idempotentRequest<T>(
key: string,
operation: () => Promise<T>
): Promise<T> {
if (processedRequests.has(key)) {
console.log(`Using cached result for ${key.substring(0, 8)}...`);
return processedRequests.get(key);
}
const result = ();
processedRequests.(key, result);
(processedRequests. > ) {
firstKey = processedRequests.().().;
processedRequests.(firstKey);
}
result;
}
() {
key = (, { title });
(key,
client..({ title })
);
}
Step 6: Bulk Document Processing with Rate Control
interface BulkProcessConfig {
documents: Array<{ title: string; pdfPath: string }>;
batchSize: number;
delayBetweenBatches: number;
}
async function bulkCreateDocuments(
config: BulkProcessConfig
): Promise<Map<string, string>> {
const results = new Map<string, string>();
const batches: Array<typeof config.documents> = [];
for (let i = 0; i < config.documents.length; i += config.batchSize) {
batches.push(config.documents.slice(i, i + config.batchSize));
}
console.log(`Processing ${config.documents.length} documents in ${batches.length} batches`);
for (let i = 0; i < batches.length; i++) {
batch = batches[i];
.();
batchResults = .(
batch.( (doc) => {
{
result = ( () => {
pdfBlob = (doc.);
client..({
: doc.,
: pdfBlob,
});
});
{ : doc., : result.!, : };
} (: ) {
{ : doc., : , : error. };
}
})
);
( result batchResults) {
(result.) {
results.(result., result.);
.();
} {
.();
}
}
(i < batches. - ) {
.();
( (r, config.));
}
}
results;
}
results = ({
: [...],
: ,
: ,
});
Output
- Reliable API calls with automatic retry
- Request queuing prevents rate limit errors
- Bulk operations optimized for throughput
- Idempotent requests prevent duplicates
Error Handling
| Scenario | Response | Action |
|---|
| 429 Rate Limited | Wait and retry | Use exponential backoff |
| Retry-After header | Honor the value | Wait specified seconds |
| Persistent 429 | Queue full | Reduce concurrency |
| 503 Service Unavailable | Temporary | Retry with longer delay |
Resources
Next Steps
For security configuration, see documenso-security-basics.