Idempotency Keys
Idempotency keys let you safely retry requests without creating duplicate renders. Send an Idempotency-Key header with a unique value — if the same key is sent again within 24 hours, PixDoc returns the cached result instead of re-rendering.
Idempotency keys are available on all plans, including Free. They are a reliability feature designed to make your integration more robust.
How to Use
Add an Idempotency-Key header to any PDF or screenshot request. The value should be a unique string that identifies the logical operation.
curl -X POST https://pixdoc.dev/api/v1/pdf \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Idempotency-Key: invoice-1234-v1" \
-H "Content-Type: application/json" \
-d '{"html": "<h1>Invoice #1234</h1>"}'Response Headers
Every response to a request with an Idempotency-Key header includes these additional headers:
| Header | Description |
|---|---|
X-Cache | HIT if the result was served from cache, MISS if a new render was performed |
X-Idempotency-Key | Echoes back the idempotency key you sent |
Behavior
| Scenario | Result |
|---|---|
| First request with a key | Renders normally, returns X-Cache: MISS |
| Same key within 24 hours | Returns cached result, X-Cache: HIT, no re-render, no quota consumed |
| Different key | New render (treated as a separate request) |
| No header | Normal render, backward compatible (no caching) |
| Key for an in-progress render | Returns 409 Conflict — retry after the first request completes |
Cached responses don't count against your render quota. If you retry a request with the same idempotency key, only the first render is billed.
Best Practices
- Use deterministic keys scoped to the logical operation. For example,
invoice-{id}-{version}ensures the same invoice version always returns the same result. - Use UUIDs for one-off requests when you just need safe retries:
const key = crypto.randomUUID(). - Keys are scoped per user. Different API keys belonging to the same user share the same idempotency namespace.
- 24-hour window. After 24 hours, the cached result expires and the same key triggers a new render.
- Don't reuse keys for different content. If you change the request body but keep the same key, you'll get the original cached result back.
Retry Example
Here's a pattern for safely retrying failed requests:
async function renderPdfWithRetry(html, idempotencyKey, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch("https://pixdoc.dev/api/v1/pdf", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
"Idempotency-Key": idempotencyKey,
},
body: JSON.stringify({ html }),
});
if (response.status === 409) {
// Key is being processed — wait and retry
await new Promise(r => setTimeout(r, 2000));
continue;
}
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.blob();
} catch (err) {
if (attempt === maxRetries - 1) throw err;
await new Promise(r => setTimeout(r, 1000 * (attempt + 1)));
}
}
}
// Safe to call multiple times — only renders once
const pdf = await renderPdfWithRetry(
"<h1>Invoice #1234</h1>",
"invoice-1234-v1"
);Key Format
Idempotency keys must be strings between 1 and 255 characters. We recommend one of these formats:
- Entity-scoped:
invoice-{id}-v{version},report-{id}-{date} - UUID:
550e8400-e29b-41d4-a716-446655440000 - Composite:
{user-id}:{operation}:{timestamp}