Rate Limits
Penvio enforces rate limits to ensure fair usage and platform stability.
API access requires a Business plan or higher.
Exceeding rate limits will result in 429 Too Many Requests responses. Implement exponential backoff to handle rate limiting gracefully.
Overview
The E-Sign API has generous rate limits for Enterprise customers:
| Limit | Value |
|---|---|
| API Requests | 10,000 requests / 15 minutes |
Rate limits are applied per API key.
Rate Limit Headers
All responses include rate limit information in headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
Example headers:
X-RateLimit-Limit: 10000
X-RateLimit-Remaining: 9995
X-RateLimit-Reset: 1705331200Rate Limit Exceeded
When you exceed the rate limit, you’ll receive a 429 Too Many Requests response:
{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests"
}
}The X-RateLimit-Reset header tells you when you can retry.
Handling Rate Limits
Implement Exponential Backoff
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
const resetTime = response.headers.get('X-RateLimit-Reset');
const waitMs = resetTime
? (parseInt(resetTime) * 1000) - Date.now()
: Math.pow(2, i) * 1000;
await new Promise(resolve => setTimeout(resolve, Math.max(waitMs, 0)));
}
throw new Error('Rate limit exceeded after retries');
}Monitor Usage
Check remaining requests before making calls:
async function checkRateLimit(apiKey) {
const response = await fetch('https://penvio.io/api/v1/esign/templates', {
method: 'HEAD',
headers: { 'Authorization': `Bearer ${apiKey}` }
});
const remaining = parseInt(response.headers.get('X-RateLimit-Remaining'));
const reset = parseInt(response.headers.get('X-RateLimit-Reset'));
return { remaining, reset };
}Queue Requests
For batch operations, queue requests to stay under limits:
class RequestQueue {
constructor(maxPerWindow = 10000, windowMs = 15 * 60 * 1000) {
this.queue = [];
this.requestCount = 0;
this.windowStart = Date.now();
this.maxPerWindow = maxPerWindow;
this.windowMs = windowMs;
}
async add(fn) {
if (Date.now() - this.windowStart > this.windowMs) {
this.requestCount = 0;
this.windowStart = Date.now();
}
if (this.requestCount >= this.maxPerWindow) {
const waitTime = this.windowMs - (Date.now() - this.windowStart);
await new Promise(resolve => setTimeout(resolve, waitTime));
this.requestCount = 0;
this.windowStart = Date.now();
}
this.requestCount++;
return fn();
}
}
// Usage
const queue = new RequestQueue();
for (const envelope of envelopes) {
await queue.add(() => createEnvelope(envelope));
}Custom Limits
Need higher rate limits? Contact your account manager or sales@penvio.io to discuss custom rate limits for high-volume use cases.
Best Practices
- Cache responses - Don’t fetch the same data repeatedly
- Use pagination efficiently - Request larger page sizes when possible
- Batch operations - Create envelopes in batches rather than one at a time
- Implement retry logic - Gracefully handle rate limits with exponential backoff
- Monitor usage - Track your API usage patterns and alert on anomalies
Next Steps
- Error Handling - Handle rate limit and other API errors
- Authentication - Manage API keys
- Subscription Tiers - Compare plan limits