ScreenshotNeo

BlogHow-to

How to Fix PDFShift Rendering JavaScript Before PDF Conversion

Use PDFShift’s wait_for function to detect when JavaScript-driven content is ready, and inject readiness code when you cannot edit the source page.

By the ScreenshotNeo team4 October 20268 min read

PDFShift can load a page’s resources before JavaScript has finished drawing its charts, applying fonts, or updating asynchronous content. Use the wait_for request parameter to name a globally available JavaScript function; PDFShift calls it repeatedly and starts the PDF conversion when it returns a truthy value. Make that function check the content your PDF actually needs. If you cannot edit the source page, provide the readiness code through PDFShift’s javascript parameter.

A fixed delay can be a useful fallback, but it cannot tell whether a chart rendered, a font loaded, or a request failed. A readiness condition tied to the required content is more precise.

1. Identify what “ready” means for your page

Choose the smallest reliable signal that proves the PDF’s required content is ready. For example, wait for a chart library’s render-complete callback, an application state flag, or the browser’s font-loading promise. Do not check only whether the page loaded if the content is created afterward.

  • Chart: Set a flag after the chart’s render promise or completion callback runs.
  • Asynchronous application data: Check for a specific populated element or an application-owned ready flag.
  • Web fonts: Wait for document.fonts.ready to resolve.
  • Images: If they are lazy-loaded, trigger their loading and check the desired image state separately.

The examples below use a global function named isPageReady. Its name is not special; it must match the value you pass as wait_for.

2. Define the readiness function in the source HTML

When you control the page, expose a global function that returns false until all required content is ready, then returns true. Here is a runnable HTML example using a simulated asynchronous chart render:

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Report</title>
  <style>
    body { font: 16px sans-serif; }
    #chart { min-height: 180px; }
  </style>
</head>
<body>
  <h1>Monthly report</h1>
  <div id="chart">Preparing chart…</div>
  <script>
    window.reportReady = false;

    // Replace this with your chart library's actual render promise or callback.
    function renderChart() {
      return new Promise((resolve) => {
        setTimeout(() => {
          document.querySelector('#chart').textContent = 'Chart rendered';
          resolve();
        }, 500);
      });
    }

    renderChart().then(async () => {
      await document.fonts.ready;
      window.reportReady = true;
    });

    // PDFShift calls this global function while waiting.
    window.isPageReady = function () {
      return window.reportReady === true;
    };
  </script>
</body>
</html>

Replace the simulated renderChart function with the chart library’s real completion signal. If several elements are required, make the condition cover all of them. Keep the function globally callable rather than hiding it inside a module scope.

3. Send the PDFShift request

Set wait_for to the global function name. The exact authentication and endpoint details depend on your PDFShift account and API version; use the current PDFShift API documentation for those request fields. The examples show the parameter relationship and should be adapted to your configured endpoint and credentials.

cURL

curl -X POST "https://api.pdfshift.io/v3/convert/pdf" \
  -H "X-API-Key: YOUR_PDFSHIFT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "https://example.com/report",
    "wait_for": "isPageReady"
  }' \
  -o report.pdf

Python

import requests

response = requests.post(
    "https://api.pdfshift.io/v3/convert/pdf",
    headers={
        "X-API-Key": "YOUR_PDFSHIFT_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "source": "https://example.com/report",
        "wait_for": "isPageReady",
    },
    timeout=120,
)
response.raise_for_status()
with open("report.pdf", "wb") as pdf:
    pdf.write(response.content)

Node.js

const response = await fetch('https://api.pdfshift.io/v3/convert/pdf', {
  method: 'POST',
  headers: {
    'X-API-Key': 'YOUR_PDFSHIFT_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    source: 'https://example.com/report',
    wait_for: 'isPageReady',
  }),
});

if (!response.ok) {
  throw new Error(`PDFShift request failed: ${response.status} ${await response.text()}`);
}

const pdf = Buffer.from(await response.arrayBuffer());
await import('node:fs/promises').then(({ writeFile }) => writeFile('report.pdf', pdf));

Check the current PDFShift documentation for the endpoint, authentication header, accepted source fields, response format, and any account-specific settings before using these request examples. The important readiness configuration is the pairing of the page’s global function with wait_for.

4. Inject readiness code when you cannot edit the page

PDFShift documents a javascript request parameter that can supply JavaScript as a string or URL. Use it to install a global readiness function and the state it checks. The readiness condition must still correspond to the target page’s actual content.

const readinessScript = `
  window.__pdfReady = false;
  window.addEventListener('load', async () => {
    try {
      await document.fonts.ready;
      window.__pdfReady = true;
    } catch (error) {
      window.__pdfReady = false;
    }
  });
  window.isPageReady = function () {
    return window.__pdfReady === true;
  };
`;

Include that script as the javascript value in the conversion request and set wait_for to isPageReady. For a chart, do not set the ready flag merely on window load: instead, check for a chart-specific marker or a visible, populated chart element. Injected code runs in the conversion context, so verify that the target element exists when the check executes.

5. Handle lazy-loaded images separately

Lazy images may not begin loading until the page scrolls near them. PDFShift’s documented approach scrolls down the page to trigger loading and then checks image completion. A basic injected check can look like this:

window.__imagesChecked = false;
window.scrollTo(0, document.body.scrollHeight);

window.isPageReady = function () {
  const images = Array.from(document.images);
  const allSettled = images.every((img) => img.complete);
  if (allSettled) window.__imagesChecked = true;
  return window.__imagesChecked;
};

img.complete becomes true for both successful and failed loads. If successful image content is required, also inspect img.naturalWidth > 0 and decide how to handle failures. Do not make one broken image block an entire conversion indefinitely. A practical policy is to wait for required images, record or tolerate optional failures, and return ready after a bounded decision point. If the page has multiple lazy-load regions, a single jump to the bottom may not trigger every site’s loading behavior; use the page’s own loading mechanism where possible.

6. Choose a readiness strategy

Strategy Use it when Tradeoff
Source-defined semantic signal You control the HTML or application code. Most precise; requires a small application change.
Injected readiness code The source page cannot be changed. Can inspect DOM state or fonts, but may not know the app’s true render lifecycle.
Fixed delay No usable readiness signal exists and rendering time is predictable. May waste time on fast pages or still be too short on slow pages.
Page-wide asset check The PDF needs a broad set of assets, such as images and fonts. Can wait on irrelevant or failed resources; define failure handling explicitly.

Prefer a semantic signal for charts and asynchronously updated data. Use page-wide checks only when the PDF really depends on all the checked resources.

7. Account for PDFShift’s conversion time budget

PDFShift’s help documentation states total conversion timeouts of 30 seconds for free accounts and 100 seconds for premium accounts. Page loading and processing consume this same total budget, so the wait has only the time remaining after those steps. These are vendor-documented limits and may change; check the current Help Center before relying on them.

Keep the readiness condition quick to evaluate and ensure it can eventually return true or follow a deliberate failure policy. Reduce unnecessary scripts and network requests. PDFShift also recommends raw HTML, inlined JavaScript and CSS, and optimized or embedded images where practical to shorten conversion time.

8. Troubleshoot missing or incomplete content

Symptom Likely cause Fix
PDF has an empty chart wait_for checks page load, not chart completion. Set a flag from the chart render completion callback or promise, and have the global function check that flag.
PDFShift does not appear to wait The function name is misspelled, is not global, or returns a truthy value too early. Expose the function on window, match its name exactly in wait_for, and return false until the required content is ready.
Conversion reaches timeout The readiness function never becomes true, resources are slow, or the wait consumes the remaining account timeout. Log the condition’s component states, add explicit handling for failed optional resources, and reduce page network work or unnecessary scripts.
Images are missing Images are lazy-loaded and the conversion did not trigger their loading. Scroll to trigger loading, then check the required images’ completion. Decide whether failed optional images should block conversion.
Web font is replaced by a fallback Font loading was not included in the readiness condition, or the font resource failed. Await document.fonts.ready and confirm the font resource is accessible to the conversion request.
Injected code cannot find the element The element is created later, selected incorrectly, or the injected script runs before it exists. Make the function check for the element on each call and return false until it exists and is populated.
PDF is valid but content is still wrong The ready condition does not represent the content requirement closely enough. Inspect the expected chart, font, or element in the output and refine the condition to test that specific result.

9. Performance, reliability, and cost considerations

  • Performance: Every extra request or script can consume conversion time. Minimize page work and wait only for required content.
  • Reliability: Make readiness checks deterministic and bounded. Handle failed images and chart errors intentionally rather than waiting forever.
  • Timeouts: Treat PDFShift’s documented 30-second free and 100-second premium total limits as the whole conversion budget, not as extra waiting time. Verify current limits with PDFShift.
  • Cost: Account timeout tier and API pricing are separate concerns. Check PDFShift’s current plan and pricing information for the cost of your usage; this guide does not infer per-conversion prices.
  • Validation: Keep a representative output check for the chart, font, or image your PDF must contain. The configuration examples explain the mechanism; they are not claims that a particular page or conversion was tested.

Or skip the browser setup

If your goal is a screenshot of the rendered page rather than a PDF, ScreenshotNeo provides a website screenshot API and MCP server. Its one-call API captures a URL as an image or PDF; see the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and each response includes X-Page-Verdict and X-Billed headers. Its MCP server gives AI agents tools to take screenshots, inspect page information, and capture PDFs. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for 1,000 free screenshots a month, with no card required.

FAQ

Does wait_for take JavaScript code?

It takes the name of a globally available function. Use PDFShift’s javascript parameter to supply code that defines the function when needed.

Should I wait for every network request to finish?

Only if every request contributes to content the PDF needs. A specific chart, font, or image condition avoids waiting on unrelated activity.

Can a failed image cause a timeout?

It can if your readiness check requires every image to succeed. Define whether each image is required, and handle failures so optional assets do not hold up the conversion.

Is a longer delay always safer?

No. A delay does not confirm that content rendered, and it uses the same total conversion budget as loading and processing. Prefer an application-specific readiness signal.