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

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

For a fixed delay in Pyppeteer, await a numeric page.waitFor() call. The value is milliseconds:

await page.waitFor(1000)  # wait for 1 second

This pauses for one second, but it does not prove that a page, element, or network request is ready. For reliable automation, wait for the condition your next step actually needs.

What a numeric page.waitFor() value means

Pyppeteer’s 0.0.25 API reference treats a numeric selectorOrFunctionOrTimeout argument as a delay in milliseconds. Therefore, await page.waitFor(1000) requests a 1,000-millisecond pause. The method is asynchronous, so omitting await will not pause the coroutine as intended. See the Pyppeteer API reference.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

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

A fixed sleep is useful when you deliberately need to give an animation, debounce, or short client-side task time to run. It is a poor substitute for a readiness check: a fast page wastes time, while a slow page can still be unfinished when the delay ends.

Common delay values

Code Requested pause Use
await page.waitFor(250) 250 milliseconds Short visual or debounce pause
await page.waitFor(1000) 1 second Simple fixed delay
await page.waitFor(5000) 5 seconds Only when a known operation needs a bounded pause

The 1,000-millisecond example is an example value, not a Pyppeteer default or performance guarantee.

Use a condition instead of guessing a delay

When the purpose of the wait is “continue when something is ready,” use a condition-based method. These waits resolve immediately if the condition is already satisfied and otherwise keep polling until success or timeout.

Wait for a CSS selector

heading = await page.waitForSelector('h1', {'timeout': 5000})

waitForSelector() waits for DOM presence by default. Set visible=True when the matching node must be visible, or hidden=True when you need it to become hidden or absent:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
await page.waitForSelector('#results', visible=True, timeout=10000)
await page.waitForSelector('.loading', hidden=True, timeout=10000)

Pyppeteer documentation supports an options dictionary and, depending on the installed version and call style, keyword arguments such as timeout=5000. Verify the signature in your installed package if one form raises a TypeError.

Wait for XPath

items = await page.waitForXPath('//ul[@id="results"]/li', {'timeout': 5000})

Use waitForXPath() when XPath expresses the target more clearly than a CSS selector.

Wait for a page condition

await page.waitForFunction('document.readyState === "complete"', {'timeout': 10000})

waitForFunction() evaluates a page function until it returns a truthy value. The documented polling choices include raf, mutation, or a numeric interval in milliseconds. A condition can be more meaningful than a sleep, but it can still time out if the condition never becomes true.

Timeout limits, units, and defaults

Do not confuse a delay with a timeout limit. In page.waitFor(1000), 1,000 milliseconds is the work you are asking Pyppeteer to perform. In waitForSelector('h1', {'timeout': 1000}), 1,000 milliseconds is the maximum time allowed for the selector to appear.

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

The Pyppeteer 0.0.25 API reference documents a 30-second default (30,000 milliseconds) for waitForSelector(), waitForXPath(), waitForFunction(), waitForRequest(), and waitForResponse(). It documents the same 30-second default for navigation methods such as goto() and waitForNavigation(). Passing 0 disables the relevant timeout. These values come from documentation dated 2018, not a promise that every newer or modified package behaves identically.

For navigation methods, setDefaultNavigationTimeout() changes the default used by navigation operations. Prefer an explicit timeout on a critical wait when the limit should be visible at the call site.

A complete Pyppeteer example

The following script follows the project’s documented launch, page creation, navigation, and shutdown pattern. Pyppeteer may download Chromium the first time it runs if a compatible browser is not already available; the project README explains that behavior at the Pyppeteer repository.

import asyncio
from pyppeteer import launch

async def main():
    browser = await launch()
    try:
        page = await browser.newPage()
        await page.goto('https://example.com')

        # Wait for the content the next step needs, with a five-second limit.
        heading = await page.waitForSelector('h1', {'timeout': 5000})
        text = await page.evaluate('(element) => element.textContent', heading)
        print(text)
    finally:
        await browser.close()

asyncio.get_event_loop().run_until_complete(main())

This script uses a five-second condition timeout, not a five-second sleep. If the h1 never appears, Pyppeteer raises a timeout exception and the finally block still closes the browser.

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

Waiting for navigation without a race

If a click or other action is expected to navigate, start the navigation wait and the action together. Starting waitForNavigation() separately can miss the navigation event, according to the API reference.

await asyncio.gather(
    page.waitForNavigation(),
    page.click('a.next'),
)

Add a selector or function wait after navigation when the destination renders important content asynchronously:

await asyncio.gather(
    page.waitForNavigation({'timeout': 30000}),
    page.click('a.next'),
)
await page.waitForSelector('#article', {'timeout': 10000})

Choosing the right wait

Need Use What it proves
Pause for a known interval await page.waitFor(milliseconds) Only that the requested time elapsed
Element exists in the DOM waitForSelector(selector) A matching node is present
Element is visible waitForSelector(selector, visible=True) The matching node meets Pyppeteer’s visibility check
Element disappears waitForSelector(selector, hidden=True) The node is hidden or absent
XPath target appears waitForXPath(xpath) An XPath match is available
Arbitrary browser state waitForFunction(expression) Your expression returns a truthy value
Action changes the document asyncio.gather(waitForNavigation(), action) The navigation event and triggering action are coordinated

Troubleshooting timeout problems

“The page still is not ready after waitFor()”

Replace the fixed delay with a selector or function that represents readiness. For example, wait for #results rather than sleeping for an arbitrary number of seconds. If the page can legitimately take longer, increase that condition’s timeout instead of repeatedly adding sleeps.

“Timeout waiting for selector”

  • Confirm the selector is correct and matches the page actually loaded.
  • Check whether the content is inside an iframe; a selector on the top-level page will not find nodes in a different frame.
  • Determine whether the element is inserted only after a click, API response, or consent interaction, and perform that prerequisite first.
  • Use visible=True only when visibility is required; the default checks DOM presence.
  • Temporarily choose a longer explicit timeout to distinguish a slow page from an incorrect condition.

“The navigation wait hangs or misses the page change”

Coordinate the wait and the action with asyncio.gather(). Also verify that the action really triggers a document navigation; single-page applications may update the DOM without firing a traditional navigation event, in which case wait for a route-specific selector or function condition.

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

“My timeout setting is ignored”

Check whether you supplied an options dictionary or keyword arguments supported by your installed Pyppeteer version. Remember that a timeout option limits a condition; it does not create a delay. A numeric argument to page.waitFor() is the delay itself.

“The browser fails before any wait runs”

Make sure Chromium can be downloaded or that launch() points to an installed executable. The project’s README and documentation landing page at pyppeteer.github.io/pyppeteer describe the project and its version history.

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

Reliability and performance practices

  • Prefer the narrowest condition that proves the next operation is safe.
  • Set explicit per-step limits so a failed page does not consume an unbounded amount of runtime.
  • Keep navigation waits and their triggering actions together to avoid event races.
  • Use a fixed delay only for a deliberate pause, such as allowing a known animation or debounce to finish.
  • Close the browser in a finally block so timeout exceptions do not leave Chromium processes running.
  • Log which wait failed and the URL being processed; this separates selector mistakes from slow or unavailable pages.

Version caveat: Pyppeteer is not current Puppeteer

The cited API reference is for Pyppeteer 0.0.25, with documentation history dated 2018-09-27. Pyppeteer describes itself as an unofficial Python port of Puppeteer and notes that JavaScript/Python differences can affect the API. Current Puppeteer documentation is useful upstream context, but it is not proof that every modern method or behavior exists in Pyppeteer. Check the installed package’s signatures and exception behavior when version-specific correctness matters. The current upstream Page API is available at github.com/puppeteer/puppeteer.

Or skip the browser setup

If your goal is simply to obtain a page image or PDF rather than interact with a browser, ScreenshotNeo provides a website screenshot API and MCP server. It handles consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients.

Free tools Windows power users keep installed

One-click scans. No signup required.

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

One GET request returns PNG, JPEG, WebP, or PDF. The API supports full-page lazy-image loading, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper and page options, custom CSS and JavaScript, clicks, selector hiding, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed image links, asynchronous jobs, webhooks, bulk capture of up to 100 URLs per call, usage data, and an OpenAPI specification.

Using the documented request format:

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

See the ScreenshotNeo API documentation for parameters and response handling. The same request in Python is:

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}`);

The Free plan includes 1,000 shots per month with no card. Paid plans are Starter $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000; yearly billing gives two months free, and every feature is included on every plan. Create a free ScreenshotNeo account to start.

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.

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