ScreenshotNeo

BlogHow-to

PDFShift Webhook Setup for Completed PDF Conversions

Configure PDFShift to notify your server when a PDF conversion finishes. Handle the queued response, callback payload, failures, and production workflow safely.

By the ScreenshotNeo team4 October 202610 min read

To receive a PDFShift completion notification, expose a server-accessible HTTPS endpoint that accepts POST requests, then include its URL in the conversion request’s webhook field. Send the conversion request as JSON to https://api.pdfshift.io/v3/convert/pdf with your API key in the X-API-Key header. The initial HTTP 202 means the conversion was accepted and queued; it does not mean the PDF is ready. PDFShift sends a later POST to your webhook when the conversion completes. PDFShift’s webhook guide documents the request, queued response, and successful callback example.

This guide builds the flow with Node.js and Express, then shows equivalent submission requests in cURL, Python, and Node.js. It also covers callback handling, security, failures, concurrency, and operational choices.

1. Understand the two HTTP requests

The integration has two separate exchanges:

  1. Your server → PDFShift: submit a source and callback URL. PDFShift responds immediately with HTTP 202 and {"success":true,"queued":true}.
  2. PDFShift → your server: after conversion, PDFShift POSTs a JSON result to the callback URL. The documented success payload includes a PDF URL and conversion metadata.

Keep the submission status and conversion outcome separate in your application. A successful 202 confirms queue acceptance, not successful rendering or callback delivery. The webhook guide’s successful payload includes success, url, filesize, duration, a nested response object, executed, and pdf_pages.

2. Prepare a reachable webhook endpoint

Your endpoint must be reachable by PDFShift from the public internet and accept HTTPS POST requests. A localhost URL works only if you expose it through a tunnel or deploy it to a public environment; a private hostname, local IP, or firewall-blocked route cannot receive the callback.

Before integrating the callback, decide what your application will do with the resulting PDF URL. Common choices are to fetch and copy the file into your own object storage, associate the URL with a conversion record, or trigger a downstream workflow. Treat callback fields as external input: validate their types and required fields, and do not let an incoming URL trigger arbitrary server-side fetches.

3. Submit a conversion with a webhook

PDFShift requires a valid API key for webhooks. Authenticate using the X-API-Key header. PDFShift’s Help Center describes this authentication mechanism and dates the move to it to May 6, 2025: PDFShift Help Center.

cURL

curl -X POST "https://api.pdfshift.io/v3/convert/pdf" \
  -H "X-API-Key: $PDFSHIFT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"source":"https://www.example.com","webhook":"https://your-domain.example/webhooks/pdfshift"}'

Expected behavior: HTTP 202 and a response body like {"success":true,"queued":true}. Store your key in a server-side secret manager or environment variable; do not embed it in browser code or commit it to source control.

Python

import os
import requests

api_key = os.environ["PDFSHIFT_API_KEY"]
payload = {
    "source": "https://www.example.com",
    "webhook": "https://your-domain.example/webhooks/pdfshift",
}

response = requests.post(
    "https://api.pdfshift.io/v3/convert/pdf",
    headers={
        "X-API-Key": api_key,
        "Content-Type": "application/json",
    },
    json=payload,
    timeout=30,
)
response.raise_for_status()
print(response.status_code, response.json())

This timeout bounds the client’s wait for the submission response; it is not a PDF conversion deadline. The callback arrives as a separate request later.

Node.js submission

const apiKey = process.env.PDFSHIFT_API_KEY;
if (!apiKey) throw new Error("Set PDFSHIFT_API_KEY");

const response = await fetch("https://api.pdfshift.io/v3/convert/pdf", {
  method: "POST",
  headers: {
    "X-API-Key": apiKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    source: "https://www.example.com",
    webhook: "https://your-domain.example/webhooks/pdfshift",
  }),
});

const body = await response.json();
if (!response.ok) {
  throw new Error(`PDFShift submission failed (${response.status}): ${JSON.stringify(body)}`);
}
console.log("Accepted:", response.status, body);

4. Receive and validate the completion callback

Here is a small Express receiver. It parses JSON, checks the documented success fields, and responds promptly. Replace the placeholder handling with durable persistence and your own download or workflow logic.

import express from "express";

const app = express();
app.use(express.json({ limit: "256kb" }));

app.post("/webhooks/pdfshift", async (req, res) => {
  const event = req.body;

  // The success example documents these fields. Be defensive about input.
  if (!event || typeof event !== "object" || Array.isArray(event)) {
    return res.status(400).json({ error: "Expected a JSON object" });
  }

  if (event.success === true && typeof event.url === "string") {
    try {
      // TODO: associate this result with your job and persist it durably.
      // Validate the URL against your expected PDFShift result host before fetching it.
      console.log("PDF ready", {
        url: event.url,
        filesize: event.filesize,
        duration: event.duration,
        pdf_pages: event.pdf_pages,
      });
      return res.sendStatus(200);
    } catch (error) {
      console.error("Could not process PDF callback", error);
      return res.sendStatus(500);
    }
  }

  // The reviewed guide does not provide a reliable failure-payload schema.
  // Record the raw, bounded payload for diagnosis and handle it without assuming fields.
  console.warn("Unrecognized or unsuccessful PDFShift callback", event);
  return res.sendStatus(200);
});

app.listen(process.env.PORT || 3000);

In production, save the callback to a durable queue or database before returning success if downstream work must not be lost. Make processing idempotent so a repeated delivery cannot create duplicate invoices, files, or notifications. The reviewed materials do not establish PDFShift’s callback retry policy or a signature-verification scheme, so do not assume either; verify the current vendor documentation before depending on delivery retries or a particular authentication header.

5. Interpret the successful callback

PDFShift’s documented success example has this shape (the values below are illustrative from the vendor example):

{
  "success": true,
  "url": "https://pdfshift.s3.amazonaws.com/d/.../result.pdf",
  "filesize": 34980,
  "duration": 1058,
  "response": {
    "status-code": 200,
    "content-length": 0,
    "requests": 0,
    "duration": 934.5173301696777
  },
  "executed": "2024-03-06T09:10:10.981098",
  "pdf_pages": 1
}
Field How to use it
success Check that it is true before treating this as a successful conversion.
url Location of the generated PDF. Persist it or retrieve the file for your application, subject to your storage and access requirements.
filesize Reported output size; useful for records and sanity checks.
duration Conversion duration metadata reported in the callback.
response Nested source-response metrics, including status code, request count, content length, and duration in the example.
executed Timestamp string in the example; parse defensively.
pdf_pages Reported number of pages in the resulting PDF.

Do not assume every callback will contain every example field or that the URL remains available indefinitely unless current PDFShift documentation confirms the applicable retention behavior. Fetch the file into storage you control when your application needs durable access.

6. Handle failures without guessing the payload

The webhook guide says conversion may fail if PDFShift cannot access the source or page loading fails, but its failure-payload example is blank. Consequently, the materials reviewed do not support a specific failure JSON schema. Build the receiver to accept unknown or incomplete JSON safely, log a bounded diagnostic record, and reconcile the job using the API or support guidance available to your account. Confirm the current failure callback shape and retry behavior with PDFShift before designing automation around them.

Keep a conversion record before submitting, with your own job identifier and a state such as submitted. When the 202 arrives, mark it queued. When a callback arrives, associate it with the right job using a correlation value if the current API supports one, then mark it complete or needing review based on documented data. Do not invent an undocumented callback field for correlation; if no supported correlation mechanism is available, use a unique callback URL path or another vendor-supported mechanism and verify it first.

7. Choose asynchronous callbacks or synchronous waiting

Approach Good fit Trade-off
Webhook Bulk work, background jobs, and workflows that should continue without holding a request open. Requires a public receiver, durable job tracking, and callback operations.
Synchronous conversion A user is waiting for a single result and the conversion fits the configured wait window. The caller remains engaged while conversion runs; timeouts and request limits matter.

PDFShift’s FAQ says parallel conversions are queued independently and each converted source produces a POST to the webhook URL. It documents a default maximum of 50 simultaneous parallel conversions, with support suggested for higher needs. The FAQ also states default conversion waits of up to 30 seconds for free plans and 100 seconds for paid plans; an overlong request returns JSON with HTTP 408. These are conversion wait details, not callback delivery timeout or retry guarantees. See the PDFShift FAQ and documentation for current service details.

8. Operational checklist

  • Use an HTTPS endpoint with a stable public DNS name and a valid certificate.
  • Keep the API key server-side and send it in X-API-Key.
  • Return the 202 submission response as queue acceptance only.
  • Persist a job record before submission and make callback processing idempotent.
  • Validate callback structure and URL before acting on it; tolerate unknown fields.
  • Keep the callback handler fast; enqueue expensive downloading or downstream work.
  • Record timestamps, job state, HTTP outcomes, and bounded error details for support diagnostics.
  • Confirm failure payloads, callback authentication, retry policy, and PDF URL retention from current vendor documentation.
  • Throttle submissions to fit the documented parallel-conversion limit or confirm a higher limit with PDFShift.

9. Troubleshooting

Symptom Likely cause Fix
Submission returns 401 or 403 Missing, invalid, or incorrectly supplied API key. Send the key in X-API-Key, check the server secret, and ensure the request is sent from your backend.
Submission returns an error instead of 202 Malformed JSON, invalid request parameters, or API authentication issue. Log the HTTP status and response body; validate the source URL and JSON before retrying.
202 arrives, but no callback appears Callback host is unreachable, route or TLS configuration is wrong, or the source conversion failed. Delivery retry behavior is not established by the reviewed materials. Check public reachability, access logs, HTTPS certificate, route, and firewall. Confirm delivery and failure behavior with PDFShift.
Callback endpoint returns 404 The configured webhook URL path does not match the deployed route. Use the exact full route in the webhook value and verify routing at the public edge.
Callback endpoint returns 400 JSON middleware is missing, body parsing failed, or handler validation is too strict. Enable JSON parsing for POST, inspect content type and request logs, and accept documented fields while tolerating additional fields.
Conversion times out with HTTP 408 Source rendering exceeded the applicable conversion wait. Reduce source-page load time and resource dependencies, then review the plan’s configured wait limits with PDFShift. A 408 concerns conversion, not webhook delivery.
Callback JSON does not match the success example It may be a failure event or payload variation; the reviewed failure example is blank. Do not assume undocumented fields. Store a safe diagnostic record and check current vendor documentation or support.
More jobs remain queued than expected Parallel work may exceed the documented default of 50 simultaneous conversions. Throttle concurrent submissions and contact PDFShift about higher capacity if required.

10. Performance, reliability, and cost

Webhooks reduce the time your own request path waits for a conversion, but they add endpoint availability and job-reconciliation work. For reliability, acknowledge only after durable receipt when you need protection against process restarts; keep expensive downloads out of the request handler; and make downstream operations safe to repeat. Since the reviewed source set does not specify webhook retry guarantees, maintain a way to identify stuck jobs and resolve them through documented PDFShift facilities or support.

Batch workloads should be paced against the documented 50 simultaneous-conversion default. Conversion wait limits are up to 30 seconds on free plans and 100 seconds on paid plans per the FAQ; slow source pages and resource-heavy rendering can raise latency or lead to HTTP 408. The research materials do not provide pricing figures, so check PDFShift’s current pricing page for the cost of your volume and plan.

11. Optional workflow automation with n8n

n8n can submit the conversion request through an HTTP Request step using POST, JSON, and X-API-Key, as shown in PDFShift’s n8n integration guide. A webhook-based flow is useful when the automation should continue after the conversion completes. Treat n8n as an optional orchestration layer: the same endpoint and callback lifecycle applies whether your application, a queue worker, or a workflow tool submits the job.

Or skip the browser setup

If your task is capturing a web page as an image or PDF rather than running a document-conversion workflow, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for options and setup.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, and cache hits are not billed. Its MCP server lets AI agents take screenshots, and the free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

FAQ

Does HTTP 202 mean my PDF is ready?

No. It means PDFShift accepted and queued the conversion. Wait for the separate completion POST.

Does the webhook contain the PDF itself?

The documented success example contains a PDF URL and metadata. Fetch or persist the file according to your application’s storage needs.

Can I use a localhost callback URL?

Not directly from a hosted conversion service. The endpoint must be reachable from PDFShift; use a public deployment or a suitable development tunnel.

Does PDFShift retry failed webhook deliveries?

The reviewed documentation does not establish retry behavior. Confirm it with PDFShift before relying on automatic redelivery.

Can I receive callbacks for many conversions at once?

Yes, each converted source produces a callback. PDFShift documents 50 simultaneous conversions by default; pace larger batches or ask about higher capacity.