Poll an async PDF render in Node with fetch
For serverless functions and request handlers that cannot hold a connection, ?wait=false returns immediately with a ticket, and the poll url carries the finished document.
The ticket answers 202 with the id, the status queued, and two urls: poll, a JSON status endpoint, and download, the artifact. Poll about once a second; the status walks queued, rendering, ready, and on a failed render the poll body carries the error. Both the poll and the download carry your key.
The recipe
import { writeFile } from "node:fs/promises";
const ORIGIN = "https://webcapture.io";
const ticket = await fetch(`${ORIGIN}/v1/convert?wait=false`, {
method: "POST",
headers: {
"Authorization": "Bearer wc_your_key",
"Content-Type": "application/json",
},
body: JSON.stringify({"url": "https://example.com/"}),
});
if (!ticket.ok) throw new Error(`convert failed: ${ticket.status}`);
// 202 {"id": "cnv_02b8c9d9", "status": "queued",
// "poll": ".../v1/conversions/cnv_02b8c9d9",
// "download": ".../v1/conversions/cnv_02b8c9d9.pdf"}
const job = await ticket.json();
let row = job;
while (row.status === "queued" || row.status === "rendering") {
await new Promise((resolve) => setTimeout(resolve, 1000));
const poll = await fetch(job.poll, {
headers: { "Authorization": "Bearer wc_your_key" },
});
row = await poll.json();
}
if (row.status !== "ready") throw new Error(row.error ?? `render ${row.status}`);
const file = await fetch(job.download, {
headers: { "Authorization": "Bearer wc_your_key" },
});
if (!file.ok) throw new Error(`download failed: ${file.status}`);
await writeFile("page.pdf", Buffer.from(await file.arrayBuffer()));
The loop stops on ready and treats anything else as a failure with the endpoint's own error message. A ready poll row adds attempts and bytes to the status, so the caller knows the size before it downloads. Renders usually finish in seconds; the one-second pace is the endpoint's own guidance, and the ticket's download url stays valid for at least seven days after the render.
Same-key GET: 401 without it; 404 for another key.
The fields this recipe uses
url: the page address to render, a public http(s) URL; ?wait=false is a query parameter on the endpoint, not a body field.
Related
- Next: Get a free key to run this Node recipe, or read the API docs.
- More recipes: Node: convert from a URL, Node: download with the key, TypeScript: convert from a URL.
- The webcapture API reference: every field the convert endpoint admits, with the response shapes and the error table.
- The recipes hub: the same endpoint in the other languages.
- Going deeper: the n8n, Zapier, and Make guides wire the same ticket in each platform's own steps.
- The free URL to PDF tool: try the render in a browser before you wire the call.
Method
Source: convert field reference, checked 2026-10-05. Corrections.