ScreenshotNeo

BlogHow-to

How to Wait for a Selector Before Taking a Browserless Screenshot

Wait for a page element to be ready before a Browserless screenshot. Learn the REST payload, visibility and timeout settings, local browser code, and fixes for common failures.

By the ScreenshotNeo team4 October 20268 min read

To wait for an element before a Browserless screenshot, send a POST request to the current REST /screenshot endpoint and put a waitForSelector condition in the JSON body. For example, wait for h1 to appear before capturing the full page:

{
  "url": "https://example.com/",
  "waitForSelector": {
    "selector": "h1",
    "timeout": 5000
  },
  "options": {
    "fullPage": true,
    "type": "png"
  }
}

The selector is a CSS selector, the timeout is in milliseconds, and the wait is a readiness gate. If you want the screenshot cropped to an element, use the screenshot-level selector option instead. A selector timeout is an API failure, so check the HTTP status and error response rather than treating every response as an image. See Browserless’s Screenshot API and request configuration documentation.

1. Choose the right kind of wait

Use a selector wait when the page exposes a reliable marker for readiness, such as a result container, chart, or heading. This is usually more precise than sleeping for a fixed duration, since it waits for a page condition rather than an estimate of how long loading will take.

Need Use What happens
Wait until an element exists, then capture the page waitForSelector Browserless waits for the condition; capture remains the configured page or viewport.
Wait until an element is displayed waitForSelector with visible: true DOM presence alone is insufficient; the element must be visible.
Capture only one element Screenshot-level selector Browserless waits for that element and crops the screenshot to its bounding box.
Wait a known amount of time waitForTimeout Waits for a time delay; use it when the behavior is genuinely time-based.
Wait for a custom page condition waitForFunction Waits for a JavaScript condition supported by the request configuration.

For a full-page capture after a content marker appears, combine waitForSelector with options.fullPage. If below-the-fold images are lazy-loaded, Browserless documents scrollPage: true as an option to trigger loading while scrolling; use it with full-page capture when appropriate.

2. Send the current REST request

The current REST API expects a JSON request body. The following cURL command sends the URL, waits up to five seconds for a visible heading, and saves a PNG response. Replace YOUR_TOKEN with your Browserless token and h1 with a selector that signals readiness on your target page.

curl --fail-with-body \
  -X POST "https://production-sfo.browserless.io/screenshot?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "url": "https://example.com/",
    "waitForSelector": {
      "selector": "h1",
      "visible": true,
      "timeout": 5000
    },
    "options": {
      "fullPage": true,
      "type": "png"
    }
  }' \
  -o page.png

Use the REST base URL and token format provided for your Browserless account. The endpoint and configuration are documented by Browserless; do not substitute a legacy payload field into this request.

Python with requests

import requests

endpoint = "https://production-sfo.browserless.io/screenshot"
params = {"token": "YOUR_TOKEN"}
payload = {
    "url": "https://example.com/",
    "waitForSelector": {
        "selector": "h1",
        "visible": True,
        "timeout": 5000,
    },
    "options": {"fullPage": True, "type": "png"},
}

response = requests.post(endpoint, params=params, json=payload, timeout=90)
if not response.ok:
    raise RuntimeError(
        f"Browserless returned HTTP {response.status_code}: {response.text}"
    )

with open("page.png", "wb") as image_file:
    image_file.write(response.content)

Node.js with fetch

const endpoint = new URL("https://production-sfo.browserless.io/screenshot");
endpoint.searchParams.set("token", "YOUR_TOKEN");

const payload = {
  url: "https://example.com/",
  waitForSelector: {
    selector: "h1",
    visible: true,
    timeout: 5000,
  },
  options: { fullPage: true, type: "png" },
};

const response = await fetch(endpoint, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify(payload),
  signal: AbortSignal.timeout(90000),
});

if (!response.ok) {
  throw new Error(
    `Browserless returned HTTP ${response.status}: ${await response.text()}`
  );
}

const image = new Uint8Array(await response.arrayBuffer());
await (await import("node:fs/promises")).writeFile("page.png", image);

3. Set the condition and capture options

Presence or visibility

By default, decide whether finding the node in the DOM is enough. If the element may exist while hidden and the capture must show it, set "visible": true in the wait condition. Visibility checks account for the element’s presence and CSS visibility or display state, as described in Browserless’s request configuration docs.

"waitForSelector": {
  "selector": "[data-state='complete']",
  "visible": true,
  "timeout": 10000
}

Prefer a stable selector intended for automation, such as a data attribute, over a class name generated by the frontend build. A selector must be valid CSS and match the rendered page, including any relevant iframe or shadow-root limitations of the page.

Timeout

Set the timeout in milliseconds according to how long the target page reasonably needs to render. The right value depends on the site and network path; there is no universal timeout or documented performance guarantee here. A timed-out selector returns a non-200 response. Choose a value that gives the page enough time without allowing stuck requests to consume your caller’s entire deadline.

Full page, lazy content, and element crops

waitForSelector does not itself crop the image. To capture just an element, use the screenshot API’s top-level selector field. For a full page, set options.fullPage to true. When content is lazy-loaded as the page scrolls, use the documented scrollPage behavior, optionally with full-page mode.

{
  "url": "https://example.com/products",
  "waitForSelector": {
    "selector": "main [data-loaded='true']",
    "visible": true,
    "timeout": 10000
  },
  "selector": "main .product-card",
  "options": { "type": "png" }
}

This example waits for a readiness marker and asks for an element capture. Ensure the wait marker and capture target represent the state you need; if there are multiple matching capture targets, verify the API’s selector behavior for your intended result.

Other documented wait choices

Browserless’s current shared request configuration also documents waitForTimeout, waitForFunction, and events. A fixed timeout can help when a page behavior is tied to a known delay, but it can be wasteful on fast loads and too short on slow ones. Use a semantic selector when the page gives you a dependable readiness signal.

4. Local Puppeteer or Playwright control

If your application connects to a browser and controls the page directly instead of using the REST screenshot endpoint, wait in the browser library before taking the screenshot.

Puppeteer

Puppeteer’s page.waitForSelector() returns immediately if the selector already exists and throws if the timeout expires. Its documented default timeout is 30 seconds; set an explicit timeout to make the behavior clear.

import puppeteer from "puppeteer";

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto("https://example.com/", { waitUntil: "domcontentloaded" });
  await page.waitForSelector("h1", { visible: true, timeout: 5000 });
  await page.screenshot({ path: "page.png", fullPage: true });
} finally {
  await browser.close();
}

For an element-only image, wait for the element and screenshot its handle, as shown in Puppeteer’s screenshots guide. See the waitForSelector API reference for selector and timeout semantics.

Playwright

Playwright supports selector waits, but its current documentation marks Page.waitForSelector as discouraged in many cases and recommends locator-based waits or web-first assertions. Use a locator when controlling a Playwright page directly:

import { chromium } from "playwright";

const browser = await chromium.launch();
try {
  const page = await browser.newPage();
  await page.goto("https://example.com/", { waitUntil: "domcontentloaded" });
  await page.locator("h1").waitFor({ state: "visible", timeout: 5000 });
  await page.screenshot({ path: "page.png", fullPage: true });
} finally {
  await browser.close();
}

These snippets control a local browser page. They are not code to put inside a Browserless REST request: for REST, express the wait in the JSON request configuration.

5. Current REST API and legacy BaaS v1

Browserless has more than one documented API generation. The current REST configuration uses waitForSelector. The legacy BaaS v1 screenshot page documents a different waitFor property that can take a selector string, a delay in milliseconds, or a page-context function. Do not combine these formats. First identify the endpoint your integration calls, then follow the matching docs.

See Browserless’s legacy /screenshot API documentation for its v1 payload. Availability of a particular endpoint can depend on the account and integration; the documentation alone does not establish what is enabled for your account.

6. Troubleshooting

Symptom Likely cause Fix
Non-200 response after waiting The selector did not match before the configured timeout. Check the rendered DOM and selector spelling, confirm the page reached the expected route, and adjust the timeout if the content legitimately takes longer.
Selector is found but screenshot looks empty The element exists but is hidden, covered, or not yet visually ready. Try visible: true; wait for a stronger ready-state marker if the page fills the element after insertion.
Screenshot is cropped unexpectedly A screenshot-level selector is being used. Remove the capture selector when you want the full page; use waitForSelector only as the readiness gate.
Images or cards below the fold are missing Lazy loading has not been triggered. Use scrollPage: true, optionally together with options.fullPage: true, as documented for screenshots.
Payload appears to ignore the wait The request uses a different API generation or an unsupported field shape. Confirm current REST versus legacy BaaS v1 and use that endpoint’s documented field names.
Blank page, CAPTCHA, or access denied The destination may be blocking automated traffic or presenting bot detection. Inspect the returned page and Browserless guidance. Its docs refer to /unblock for some bot checks, but this is not a guaranteed fix.
Client reports JSON parse or image decode error The response may be an API error body rather than an image. Check HTTP status and content before saving or decoding the response as an image.

7. Performance, reliability, and cost

A selector wait avoids guessing a fixed delay, but the timeout still sets an upper bound for how long the request may wait on that condition. Keep the wait tied to the specific content needed for the capture. Waiting for a page-wide condition that never settles can make a useful screenshot fail; waiting for a marker that appears before the relevant content is rendered can produce an incomplete image.

Handle the non-200 timeout path, set an outer HTTP deadline longer than the intended selector wait plus capture and response time, and log the requested URL, selector, timeout, HTTP status, and error body. Do not log tokens. Browserless’s retrieved docs provide no selector-wait latency benchmark or fixed success rate, so measure behavior against your own target pages. The cost of Browserless usage depends on the account and plan; consult its current account pricing rather than assuming a per-screenshot amount.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. Make one GET request for a PNG, JPEG, WebP, or PDF; the API can also wait for a selector while you choose whether the output is a full page or an element capture.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com \
  -d wait_for_selector='h1' \
  -o shot.webp

See the ScreenshotNeo API docs for request options. Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up free for ScreenshotNeo.

FAQ

Does a selector wait capture only that element?

No. It gates when screenshot work starts. Use the screenshot-level selector when the image itself should be cropped to an element.

What does the timeout number mean?

It is a duration in milliseconds. If the condition is not met in time, Browserless documents a non-200 response.

Can I use the REST payload with Puppeteer?

No. A REST request uses Browserless’s JSON configuration. Puppeteer and Playwright waits are methods called on a page object you control directly.

Should I use a fixed delay instead?

Use a selector when a known element indicates readiness. Use a delay only when the behavior is inherently time-based and there is no suitable page-state signal.