ScreenshotNeo

BlogHow-to

How to Wait for Network Idle in Puppeteer

Use Puppeteer's network-idle waits correctly, choose the right timeout and threshold, and diagnose pages that never become quiet.

By the ScreenshotNeo team4 October 20266 min read

Use await page.waitForNetworkIdle() to wait for Puppeteer to observe a quiet network after navigation or another action. Its documented defaults are zero concurrent connections and a 500 ms quiet period. For a navigation-specific wait, pass waitUntil: 'networkidle2' (at most two connections for at least 500 ms) or 'networkidle0' (zero connections for at least 500 ms) to page.goto(). Network quiet is not proof that a particular application task has completed; if you need a specific element or state, wait for that condition directly.

1. Install Puppeteer and run a minimal example

Install Puppeteer in a Node.js project, then save this as capture.js. The script navigates, waits for network idle, writes a screenshot, and closes the browser even if an operation fails.

npm install puppeteer
// capture.js
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  try {
    const page = await browser.newPage();
    await page.goto('https://example.com', { waitUntil: 'networkidle2' });
    await page.screenshot({ path: 'screenshot.png', fullPage: true });
  } finally {
    await browser.close();
  }
})().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Use networkidle2 when up to two connections may remain during the idle window. Choose networkidle0 if the navigation should wait for zero connections under Puppeteer’s lifecycle definition. The official screenshot guide uses networkidle2 before a screenshot. Puppeteer screenshot guide; lifecycle event definitions.

2. Wait directly, or wait as part of navigation

These are two different APIs for related situations. A page.goto() lifecycle option waits for the selected navigation condition. page.waitForNetworkIdle() starts a network-idle wait at the point you call it, including after a prior navigation or interaction.

Wait during navigation

await page.goto('https://example.com', {
  waitUntil: 'networkidle2',
  timeout: 30_000,
});

Wait after navigation or an interaction

await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
await page.waitForNetworkIdle();

The direct method resolves to Promise<void>. Its documented options default to concurrency: 0 and idleTime: 500 milliseconds, and it always waits at least the configured idle interval. Page.waitForNetworkIdle API; WaitForNetworkIdleOptions.

3. Configure the quiet interval, concurrency, timeout, and cancellation

Use concurrency for the maximum number of concurrent connections still treated as idle, and idleTime for the required quiet period in milliseconds. The values below illustrate configuration; they are not universal recommendations.

await page.waitForNetworkIdle({
  concurrency: 2,
  idleTime: 1_000,
  timeout: 30_000,
});

The shared wait options document a default timeout of 30 seconds. You can set a different timeout, set it to 0 to disable the timeout, or provide an AbortSignal to cancel the wait. Disabling a timeout can leave a script waiting indefinitely if the page never reaches the requested condition, so use it only when your surrounding workflow has another way to stop or bound execution. WaitForOptions API.

const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 15_000);

try {
  await page.waitForNetworkIdle({
    idleTime: 750,
    timeout: 20_000,
    signal: controller.signal,
  });
} finally {
  clearTimeout(timer);
}

When choosing values, start with the page’s observable behavior and the task’s actual completion requirement. A longer idle interval asks for a longer quiet period; a nonzero concurrency threshold allows more connections to remain active. Neither setting establishes that a particular widget, image, or application state is ready.

4. Choose a wait condition that matches the task

Wait Condition Use when
networkidle0 Zero connections for at least 500 ms The navigation should reach complete quiet under this lifecycle definition.
networkidle2 At most two connections for at least 500 ms Allowing up to two connections is appropriate; this is used in Puppeteer’s screenshot guide.
page.waitForNetworkIdle(options) Configurable concurrency and idle interval You need a direct wait or need to tune those thresholds.
Locator or application-specific condition A particular element or state is ready The task depends on a visible, enabled, or otherwise actionable target rather than general network activity.

For example, if the goal is to click a button after it becomes available, express that goal with a locator:

const button = page.locator('button.submit');
await button.click();

Puppeteer locators wait for elements and relevant action preconditions. Choosing a targeted wait when the task depends on a specific UI state is a practical inference from the different documented purposes of network-idle waits and locators. See the Puppeteer interactions guide.

5. Troubleshoot timeouts and premature captures

Symptom Likely cause What to do
waitForNetworkIdle() times out Traffic never stays below the chosen concurrency threshold for the configured idle interval, or the timeout is too short for this workflow. Inspect the page’s network activity; use a threshold that fits the page, increase the timeout if loading legitimately takes longer, or wait for the task’s specific target instead.
networkidle0 never completes The zero-connection condition is stricter than the page can satisfy during the wait. Try networkidle2 if allowing up to two connections fits the task, or use a targeted condition.
The screenshot is taken before the desired content appears Network quiet can occur before the application-specific state is ready. Wait for the relevant element or state, then capture. Network idle describes traffic, not application completion.
The script appears stuck after setting timeout: 0 The wait has no timeout bound. Restore a finite timeout or add an external cancellation path using an abort signal.
Navigation fails before the idle wait The navigation itself did not complete successfully, so the subsequent wait is never reached. Handle the navigation error separately, confirm the target URL is reachable from the browser environment, and log which operation failed.

Keep navigation and subsequent waits in separate try/catch boundaries when you need to distinguish a navigation failure from a network-idle timeout. Always close the browser in a finally block so a failed wait does not leave a browser process running.

6. Performance, reliability, and cost considerations

A network-idle wait adds at least its configured quiet interval after the relevant traffic falls within the threshold. Longer quiet intervals and stricter thresholds can increase waiting time; looser thresholds may let the workflow continue sooner but do not establish that the page’s desired content is ready. Use the smallest condition that accurately represents the task, and keep a finite timeout for unattended jobs.

For repeated captures, choose the same condition consistently and record whether failure occurred during navigation or during a later wait. This makes timeouts easier to diagnose without treating a network-idle timeout as proof that the site is unavailable. Puppeteer runs a browser under your control, so account for browser startup, resource use, and operational maintenance in your own environment. The research dossier provides no benchmark or cost figures for those items.

7. Or skip the browser setup

If your goal is simply to get a website screenshot, ScreenshotNeo returns an image or PDF from one API request. Its response identifies page verdict and billing status; only clean shots are billed, while bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture, and each cleanup step can be turned off. It also offers an MCP server with screenshot, page-info, and PDF tools for AI agents.

Install the Python dependency with python -m pip install requests, set SCREENSHOTNEO_API_KEY to your key, then run:

import os
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={
        "access_key": os.environ["SCREENSHOTNEO_API_KEY"],
        "url": "https://example.com",
    },
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

See the ScreenshotNeo API documentation for request options. The service also supports full-page captures with lazy images loaded, element capture, device and viewport settings, retina scale, PDF options, custom CSS and JavaScript, waits, request blocking, custom headers and cookies, geolocation, caching, signed public image links, asynchronous jobs, bulk capture, and a usage API. Puppeteer-style parameter names used by other screenshot APIs also work to make switching easier.

Plans include 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan. Sign up free for 1,000 screenshots a month with no card.

8. FAQ

How long does Puppeteer wait for network idle?

The lifecycle events networkidle0 and networkidle2 use a 500 ms interval. The direct method defaults to idleTime: 500 ms and always waits at least its configured interval; the shared wait timeout defaults to 30 seconds.

Should I use networkidle2 or page.waitForNetworkIdle()?

Use networkidle2 as a navigation condition in page.goto(). Use the direct method when you need to wait separately from navigation or configure the idle threshold and duration.

Does network idle guarantee all images or scripts are finished?

No. It indicates that observed network activity met the chosen threshold and quiet interval. If your task depends on particular content or an interactive state, wait for that target explicitly.

Can I cancel a direct network-idle wait?

Yes. The shared wait options support an AbortSignal. You can also bound the wait with its timeout option.