Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

There is no single Puppeteer wait that proves a very large PDF has finished decoding and painting every page. Choose a signal that matches what you opened: an HTML page, a direct PDF response, or an application PDF viewer. Navigation lifecycle events and network-idle waits tell you about document and network activity; only a viewer-specific readiness signal can reliably describe that viewer’s parsing and display work.

First identify what the URL returns

The same-looking PDF workflow can involve three different browser states. Inspect the response and application before choosing a wait.

Setup What Puppeteer can observe What “finished” means Best starting strategy
Normal HTML page with a PDF link HTML document lifecycle, selectors and requests The page is usable and the link is available Navigate with the least strict lifecycle event that meets your need, then wait for the link or application state
Top-level navigation directly to a PDF URL Browser navigation and response behavior, subject to headless-mode support The browser accepted the PDF document; this is not a universal promise that every page is painted Confirm the selected headless mode supports direct PDF navigation before tuning timeouts
PDF inside an application-controlled viewer Viewer DOM, application flags, selectors and JavaScript state The application says its parsing/rendering work is complete Wait for the viewer’s own completion signal when one exists; otherwise define a bounded, application-specific fallback

Puppeteer’s headless shell does not support navigation to a PDF document. A timeout on a direct PDF URL can therefore be a browser-mode limitation rather than a slow file. Use a headless mode that supports the navigation, or process the file outside the browser.

What Puppeteer’s built-in waits actually guarantee

domcontentloaded

This fires when the HTML document has been parsed. Referenced resources may still be downloading, so it is not a PDF or image completion signal.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

load

This waits for the document’s load event and its load-event resources. It is stronger than domcontentloaded for an HTML application, but it still does not establish that a PDF viewer has decoded and painted all pages.

networkidle0 and networkidle2

Puppeteer’s navigation lifecycle variants use a 500 ms quiet window. networkidle0 requires no active connections; networkidle2 allows up to two. Pages with analytics, streaming, polling or long-lived sockets can make networkidle0 slow or impossible. Conversely, a quiet network can occur while a viewer is still parsing a downloaded PDF.

page.waitForNetworkIdle()

This method waits until Puppeteer considers the network idle, using a configurable idle time (the documented default is 500 ms) and concurrency. It is useful as an additional settling period, not as proof of PDF-render completion. Its promise resolves because requests are quiet, not because every PDF page is visible.

Waiting for an ordinary HTML application page

If the URL is an HTML page that eventually exposes a PDF link or viewer element, start with a lifecycle condition appropriate to the page, then wait for an observable application condition.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({headless: true});
const page = await browser.newPage();

await page.goto('https://example.com/report', {
  waitUntil: 'load',
  timeout: 120_000,
});

await page.waitForSelector('[data-pdf-ready="true"]', {
  timeout: 120_000,
});

// Work with the now-ready application.
await browser.close();

The selector above is an example contract owned by the application, not a universal PDF-viewer selector. Replace it with a real element that your application sets only after it has completed the required work.

When network quiet is a useful secondary signal, begin the idle wait alongside navigation so it observes the navigation’s requests:

await Promise.all([
  page.goto('https://example.com/report', {
    waitUntil: 'domcontentloaded',
    timeout: 120_000,
  }),
  page.waitForNetworkIdle({
    idleTime: 1_000,
    concurrency: 0,
    timeout: 120_000,
  }),
]);

await page.waitForSelector('[data-pdf-ready="true"]', {
  timeout: 120_000,
});

The 120-second and 1-second values are illustrative limits. Choose them from your file sizes, server behavior and job deadline; they are not guarantees supplied by Puppeteer.

Waiting on an application-controlled PDF viewer

An embedded viewer may download ranges, parse incrementally and paint pages after the network becomes quiet. Ask the viewer integration for an explicit state: a loaded flag, a page-count value, an “idle” event bridged into the page, or a selector that appears only after rendering is complete.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Selector-based readiness

await page.waitForSelector('#pdf-viewer[data-state="complete"]', {
  visible: true,
  timeout: 180_000,
});

Function-based readiness

await page.waitForFunction(
  () => {
    const viewer = window.myPdfViewer;
    return viewer && viewer.status === 'complete' && viewer.pageCount > 0;
  },
  {timeout: 180_000, polling: 'mutation'},
);

Use the real global, property names and state transitions documented by your viewer. Do not copy a selector from another product and assume it works everywhere. If the application exposes an event rather than state, have the page set a deterministic flag when that event fires, then wait for the flag.

When no readiness signal exists

Define what your job actually needs. If you need the first page, wait for that page’s canvas or image. If you need a page count, wait for the count and verify it is the expected value. If you need a screenshot of a particular page, wait for that page’s render marker instead of all-document idle. A bounded delay can be a last-resort safety margin, but it is probabilistic and should be paired with a verification check and a timeout.

Direct navigation to a PDF URL

Before debugging a “large file” timeout, confirm the browser mode. Puppeteer documents that headless shell cannot navigate directly to a PDF document. A supported browser mode may expose a PDF viewer, but lifecycle completion still describes navigation rather than guaranteed full decoding and painting.

const response = await page.goto('https://example.com/large.pdf', {
  waitUntil: 'load',
  timeout: 180_000,
});

if (!response) {
  throw new Error('No navigation response was returned');
}

console.log(response.status(), response.headers()['content-type']);

Check that the response is actually a PDF (for example, an appropriate content type), that redirects are expected, and that authentication headers or cookies are present. If your goal is to extract text or inspect page geometry, browser navigation waits are the wrong contract: download the bytes and use a PDF parser designed for that task.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

page.pdf() is a different operation

page.pdf() creates a PDF from the current web page. It does not open a remote PDF URL and wait for Chrome’s PDF viewer. Puppeteer documents that PDF generation waits for fonts by default and has its own options and timeout behavior (the documented default for PDF generation is 30,000 ms). Do not transfer that default to goto, waitForSelector or waitForNetworkIdle; each API has separate timeout settings.

await page.goto('https://example.com/invoice', {
  waitUntil: 'networkidle2',
  timeout: 120_000,
});
await page.pdf({
  path: 'invoice.pdf',
  printBackground: true,
  waitForFonts: true,
});

If you are printing HTML, make the HTML application’s readiness condition explicit before calling page.pdf(). If you are viewing an existing PDF, use the direct-navigation or embedded-viewer strategies instead.

Timeouts, performance and reliability

  • Set independent budgets. Give navigation, network-idle settling and viewer readiness separate timeouts so an unresponsive stage is identifiable.
  • Prefer application state over long sleeps. A state check finishes quickly for small files and remains safe for large ones; a fixed delay either wastes time or expires too early.
  • Expect incremental loading. Large PDFs can request byte ranges and render pages progressively. Network quiet may happen between range requests.
  • Limit work to the required page. If the job only needs page 1, do not wait for a document-wide condition your viewer cannot provide.
  • Capture diagnostics. Record the URL after redirects, response status and content type, browser/headless mode, elapsed time, and the last observed viewer state.
  • Use bounded retries carefully. Retry transient network failures, but do not retry an unsupported headless mode; change the mode or processing approach.

Troubleshooting common failures

“Navigation timeout exceeded” on a PDF URL

First check whether you launched headless shell, which does not support direct PDF navigation. Then inspect redirects, authentication and response headers. Increasing the timeout cannot fix an unsupported mode or a server that never completes.

networkidle0 never resolves

Look for polling, analytics, WebSockets or streaming requests. Use networkidle2, configure waitForNetworkIdle with an appropriate concurrency value, or rely on the viewer’s readiness state.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Network idle resolves but pages are blank

This is expected when network quiet precedes PDF decoding or painting. Wait for a viewer-owned page/canvas marker or state flag and verify the target page’s dimensions or rendered content.

waitForSelector times out

Confirm the selector exists in the correct frame. Embedded viewers often use an iframe or shadow DOM; obtain the frame and wait there, or ask the application to expose a readiness flag in the top-level page. Also verify that the state transition actually occurs on failures.

The script works headed but not headless

Compare browser versions and headless modes, then collect console and page-error messages. A mode that lacks PDF navigation support or behaves differently with GPU/compositing cannot be repaired by a longer wait alone.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server when you need an image or PDF of a URL without maintaining Puppeteer. It accepts cookie/consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing result.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

One GET request is enough (see the ScreenshotNeo API documentation):

Best Value
The SQL Programming Language: .
  • Used Book in Good Condition
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

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)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

It also offers an MCP server with take_screenshot, get_page_info and capture_pdf for Claude, Cursor and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account to try it.

Frequently Asked Questions

Can network-idle waits prove that every page of a large PDF is rendered?

No. They measure request activity. A PDF viewer can still be decoding or painting after the network is quiet.

Should I use a longer fixed delay instead of a readiness signal?

Only as a bounded fallback. A viewer-owned state or page-specific render marker is more reliable and usually faster.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What if I need PDF text rather than a screenshot?

Download the PDF and process it with a PDF parser; Puppeteer navigation waits are not a PDF parsing contract.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.