Rate limits
The DocGen API enforces per-plan throughput limits to keep generation latency predictable for every customer on the platform. Limits apply to the org that owns the API credentials, aggregated across all clients using those credentials.
This page documents the DocGen-specific limits. For the platform-wide request-rate policy that applies to all Propper APIs, see the global rate limits guide.
Per-plan limits
| Plan | Documents/hr | Burst/min | Notes |
|---|---|---|---|
gen_starter | 60 | 1 | SMB tier |
gen_pro | 600 | 10 | Mid-market |
gen_hoa | 600 | 10 | Burst weighted toward bulk_send |
business | 1,200 | 20 | Existing full-suite plan |
gen_payg | 6,000 | 100 | Metered Pay-As-You-Go |
A "document" is one successful generation that produces a stored artifact. Failed generations that return a 4xx before any rendering work begins do not count against the hourly budget. Failures that occur after a render starts (template errors, asset fetch failures) do count, because they consume worker capacity.
The burst-per-minute column caps short-window throughput so that a single client cannot drain the hourly budget in seconds and starve other consumers. Hitting the burst cap returns a 429 with a short retryAfterSeconds even when the hourly budget is not exhausted.
429 response shape
When a request is throttled the API returns HTTP 429 Too Many Requests with the following JSON body:
{
"code": "rate_limited",
"message": "Rate limit exceeded for plan gen_starter.",
"details": {
"plan": "gen_starter",
"limit": 60,
"windowSeconds": 3600,
"retryAfterSeconds": 287
}
}
Every 429 response also carries these headers:
| Header | Description |
|---|---|
Retry-After | Seconds to wait before the next request. Mirrors details.retryAfterSeconds. |
X-RateLimit-Limit | Maximum documents allowed in the current window. |
X-RateLimit-Remaining | Documents remaining in the current window. Will be 0 on a 429. |
X-RateLimit-Reset | Unix timestamp (seconds) at which the current window resets and the full budget becomes usable. |
These headers are also returned on successful (2xx) responses so a well-behaved client can pace itself without ever hitting a 429.
Backoff guidance
When a 429 is returned, use exponential backoff seeded from details.retryAfterSeconds:
- Wait
retryAfterSecondsbefore the first retry. - Double the wait on each subsequent retry:
retryAfterSeconds * 2^attempt. - Apply ±10% jitter to each wait to avoid synchronized retries across workers.
- Cap at 5 retries. After the fifth failure, surface the error to the caller rather than continuing to retry.
async function generateWithBackoff(request: () => Promise<Response>) {
const maxRetries = 5;
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await request();
if (response.status !== 429) {
return response;
}
const retryAfter = Number(response.headers.get('Retry-After') ?? '1');
const base = retryAfter * Math.pow(2, attempt);
const jitter = base * (0.9 + Math.random() * 0.2);
await new Promise((resolve) => setTimeout(resolve, jitter * 1000));
}
throw new Error('Rate limit retries exhausted');
}
Batch endpoints (POST /batches) consume a single hourly slot per batch on submission, even though the batch may produce hundreds of documents. Prefer batching for any flow that generates more than ~10 documents at once. See the batch guide for examples.
Requesting a limit increase
If your usage pattern outgrows the limits for your current plan, you have two options:
- Self-serve upgrade. Org admins can compare plans and upgrade in the dashboard at
https://app.propper.ai/organization/settings/billing. New limits take effect within a minute of the upgrade webhook completing. - Custom limits. For sustained throughput above the
gen_paygrow, contact your account team or reach out through the support channel listed in your contract. Custom limits are provisioned per-org and do not require a plan change.
Limit increases are not granted as a workaround for a misbehaving integration. If you are hitting limits because of a tight retry loop or polling-instead-of-webhooks pattern, the support team will surface that before changing the cap.