Async Polling
When a PDF is too large for synchronous processing or you explicitly request async mode, PDFPipe queues the job and returns a poll URL. This guide explains the flow and shows how to implement it.
When does async processing happen?
Async processing is used when either:
- The PDF file is 10 MB or larger
- You set
"async": truein the request body - Synchronous processing exceeds your
timeout(1–60 seconds) — the API queues the job and may include amessageon the 202 response - You use the batch endpoint (always async)
The API returns 202 Accepted instead of 200, with a pollUrl you can use to check progress.
The async flow
1. Submit the request
curl -X POST https://api.pdfpipe.dev/v1/convert \
-H "Authorization: Bearer pk_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/large-report.pdf",
"format": "json",
"async": true
}'2. Receive the poll URL
{
"requestId": "req_01J9X7K2M...",
"status": "pending",
"pollUrl": "/v1/status/req_01J9X7K2M..."
}If you set webhook on the convert request, the body may also include webhook.url and webhook.secret. After a timeout-driven fallback to the queue, a message field may explain why the job is async.
{
"requestId": "req_01J9X7K2M...",
"status": "pending",
"pollUrl": "/v1/status/req_01J9X7K2M...",
"webhook": { "url": "https://example.com/hooks/pdfpipe", "secret": "whsec_..." },
"message": "Example when the job was queued after exceeding your timeout."
}3. Poll for status
Make GET requests to /v1/status/:requestId every 5 seconds (append ?returnMethod=inline if you want content in the JSON when status is complete). The status progresses through:
{
"requestId": "req_01J9X7K2M...",
"status": "processing",
"format": "json",
"type": "inline",
"createdAt": "2026-02-22T11:00:00.000Z",
"updatedAt": "2026-02-22T11:00:02.500Z"
}{
"requestId": "req_01J9X7K2M...",
"status": "complete",
"format": "json",
"type": "inline",
"detectedType": "inline",
"pagesProcessed": 48,
"creditsUsed": 1,
"resultUrl": "https://pdfpipe-results.s3...",
"expiresAt": "2026-02-23T11:00:00.000Z",
"processingDurationMs": 12450,
"createdAt": "2026-02-22T11:00:00.000Z",
"updatedAt": "2026-02-22T11:00:12.450Z"
}4. Handle failures
If the status is failed, the response includes an error object with a code, message, and optional suggestion:
{
"requestId": "req_01J9X7K2M...",
"status": "failed",
"format": "json",
"type": "inline",
"error": {
"code": "PROCESSING_ERROR",
"message": "Processing failed. Please retry or contact support.",
"suggestion": "Ensure the URL is publicly accessible and returns a valid PDF."
},
"createdAt": "2026-02-22T11:00:00.000Z",
"updatedAt": "2026-02-22T11:00:08.000Z"
}Timeout instead of guessing sync vs async
Set timeout to the maximum seconds (1–60) you are willing to wait inline. If the converter cannot finish in that window, you receive 202 and poll (or use a webhook) as usual. This avoids choosing between sync and async up front for borderline files.
Webhooks (no polling)
Pass webhook: { url, secret? } on POST /v1/convert. When the job is asynchronous, the 202 body includes webhook.url and webhook.secret (generated if you omitted secret). PDFPipe POSTs to your HTTPS endpoint when processing completes; verify payloads with HMAC-SHA256 using the X-PDFPipe-Signature: sha256=... header (see the API Reference).
Polling best practices
- Poll every 5 seconds. Most conversions complete within 10–30 seconds.
- Set a timeout. Stop polling after 5 minutes (60 attempts) and treat it as a failure.
- Check for terminal states. Stop polling when status is
completeorfailed. - Download the result promptly. The
resultUrlis a presigned link; each signed URL expires after a tier-based TTL, and the API stops issuing new URLs after your plan's result retention window from request creation (1 hour to 30 days).
Full Node.js example
async function convertPdf(apiKey: string, pdfUrl: string) {
// 1. Submit the request
const res = await fetch("https://api.pdfpipe.dev/v1/convert", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ url: pdfUrl, format: "json", async: true }),
});
const data = await res.json();
// 2. If synchronous, return immediately
if (res.status === 200 && data.resultUrl) {
return data;
}
// 3. Poll until complete or failed
const pollUrl = `https://api.pdfpipe.dev/v1/status/${data.requestId}`;
const maxAttempts = 60;
for (let i = 0; i < maxAttempts; i++) {
await new Promise(r => setTimeout(r, 5000));
const poll = await fetch(pollUrl, {
headers: { "Authorization": `Bearer ${apiKey}` },
});
const status = await poll.json();
if (status.status === "complete") return status;
if (status.status === "failed") throw new Error(status.error.message);
}
throw new Error("Polling timed out");
}