ScreenshotNeo

BlogHow-to

How to Set a Wait Condition for a Dynamic Page in a Website Screenshot API

Choose a wait condition that matches how your page becomes ready: a stable selector, network idle, or a short fixed delay.

By the ScreenshotNeo team4 October 20267 min read

For a dynamic page, wait for the thing your screenshot depends on: use a selector wait when a stable element marks readiness, a documented network-idle condition when several requests populate the page, or a short delay as a bounded buffer for known late UI. A fixed delay alone cannot confirm that content exists. Check the screenshot provider’s current documentation for exact parameter names, supported values, defaults, and timeout limits; they vary by API.

Choose the wait condition

Start by defining what “ready” means for the image. If the capture must include a chart, result list, or dashboard panel, the chart or panel being present is more useful than waiting for an unrelated lifecycle event. If many requests jointly populate the page and no single element is dependable, use a supported network-idle strategy and keep a finite timeout.

Page behavior Start with Trade-off
A known component appears when the needed content is ready waitForSelector or the provider’s equivalent Targeted and often faster; the selector must remain valid and satisfy visibility requirements.
Several asynchronous requests populate the view A documented network-idle mode, such as networkidle0 or networkidle2 Useful when requests drive rendering; persistent background traffic can delay or prevent settling.
A known animation or late UI needs a little extra time A short delay after a readiness condition, if supported Predictable buffer, but it does not verify content by itself.
Only early document structure is required domcontentloaded, if supported Earlier milestone; JavaScript-rendered content may not be ready yet.

Cloudflare’s Browser Run guidance notes that the default page-load behavior can return empty or incomplete results for JavaScript-heavy pages and SPAs. It also documents waiting for a specific element as an option when you do not need to wait for all network activity to stop. See the Cloudflare screenshot endpoint guide.

How to configure a wait

  1. Identify the capture dependency. Pick a stable element that appears only when the content you need is rendered. Prefer an application-owned selector over fragile positional selectors.
  2. Choose the earliest reliable navigation milestone. If the endpoint supports it, a fast milestone such as domcontentloaded can be followed by a selector wait. Use network idle when that better describes page readiness.
  3. Set finite timeouts. Bound navigation and selector waits so a stalled page or persistent requests cannot occupy a capture indefinitely.
  4. Add a delay only for a known reason. A brief buffer can help after a selector appears if a known animation or paint still needs to finish. Keep it bounded and avoid treating it as proof of readiness.
  5. Check the returned response. Confirm that the response is an image or PDF as expected and inspect provider-specific status or diagnostic headers if the result is blank or incomplete.

Parameter names and semantics are vendor-specific. For example, Screenshot API documents waitUntil, waitForSelector, and delayMs; Cloudflare uses gotoOptions.waitUntil, waitForSelector, and waitForTimeout. Do not copy one provider’s request fields into another provider’s endpoint.

Cloudflare Browser Rendering request example

This conceptual JSON request combines an early navigation milestone with a content-specific selector. Replace the URL, selector, and timeouts for your page, and verify the current endpoint version and authentication requirements in the Cloudflare screenshot API reference.

{
  "url": "https://example.com/dashboard",
  "gotoOptions": {
    "waitUntil": "domcontentloaded",
    "timeout": 45000
  },
  "waitForSelector": {
    "selector": "#dashboard-content",
    "visible": true,
    "timeout": 15000
  }
}

The values above are illustrative, not universal performance recommendations. Cloudflare’s referenced schema documents its own limits, including a maximum 60-second navigation timeout and selector or fixed waits up to 120 seconds. Always consult the current API reference before deploying a request.

Screenshot API wait options

Screenshot API’s official documentation lists waitUntil, waitForSelector, and delayMs. It documents networkidle2 as the current waitUntil default, delayMs defaulting to 0, and timeoutMs defaulting to 30,000 milliseconds. The names and defaults are specific to that service and may change; confirm them in the Screenshot API documentation before using its endpoint.

cURL, Python, and Node.js request patterns

For any provider, use its documented endpoint, authentication, and request format. These are request-shape examples for a JSON API; replace the placeholder endpoint and authentication with the provider’s current values. The Cloudflare guide and reference linked above define Cloudflare’s actual request fields.

cURL

curl -X POST "https://api.example.com/screenshot" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "url": "https://example.com/dashboard",
    "gotoOptions": {"waitUntil": "domcontentloaded", "timeout": 45000},
    "waitForSelector": {
      "selector": "#dashboard-content",
      "visible": true,
      "timeout": 15000
    }
  }' \
  -o dashboard.png

Python

import requests

endpoint = "https://api.example.com/screenshot"  # Replace with your provider's endpoint.
payload = {
    "url": "https://example.com/dashboard",
    "gotoOptions": {"waitUntil": "domcontentloaded", "timeout": 45000},
    "waitForSelector": {
        "selector": "#dashboard-content",
        "visible": True,
        "timeout": 15000,
    },
}
response = requests.post(
    endpoint,
    headers={"Authorization": "Bearer YOUR_API_TOKEN"},
    json=payload,
    timeout=90,
)
response.raise_for_status()
with open("dashboard.png", "wb") as image_file:
    image_file.write(response.content)

Node.js

const endpoint = 'https://api.example.com/screenshot'; // Replace with your provider's endpoint.
const response = await fetch(endpoint, {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_API_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    url: 'https://example.com/dashboard',
    gotoOptions: { waitUntil: 'domcontentloaded', timeout: 45000 },
    waitForSelector: {
      selector: '#dashboard-content',
      visible: true,
      timeout: 15000,
    },
  }),
});
if (!response.ok) {
  throw new Error(`Screenshot request failed: ${response.status} ${await response.text()}`);
}
const image = Buffer.from(await response.arrayBuffer());
await import('node:fs/promises').then(({ writeFile }) => writeFile('dashboard.png', image));

These generic examples assume the endpoint returns image bytes directly. Some APIs instead return JSON, a job identifier, or a download URL. Follow the chosen provider’s response format and authentication documentation.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. Its single GET request captures a URL, and its wait options include waiting for a selector, a delay, or network idle. See the ScreenshotNeo API documentation for the current parameters and response details.

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

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. 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.

Troubleshooting

The screenshot is blank or misses JavaScript content

The capture may occur at a navigation milestone that comes before client-side rendering or data requests finish. Wait for a stable selector that marks the content, or use the provider’s supported network-idle mode if several requests jointly populate the page.

The selector wait times out

Check that the selector exists in the rendered page, matches the correct capitalization and page state, and satisfies the requested visibility condition. The element may be inside a frame the endpoint does not inspect, behind authentication, or absent in the requested locale or session. Use a stable selector and verify the target page state.

Network idle takes too long

Some pages keep background requests open or continuously issue new ones. Prefer a specific selector when it reliably indicates readiness. Keep navigation and selector waits finite and use the documented timeout fields.

The capture is still slightly early

If the content is present but a known animation or late paint is incomplete, add a small documented delay after the readiness condition. A delay is a buffer, not a content check; keep it short and validate the result against the actual page.

The request rejects a parameter or behaves differently

Confirm the exact endpoint version, field names, supported waitUntil values, visibility semantics, defaults, and timeout limits. Similar APIs use different names and limits. For example, the cited Cloudflare schema and Screenshot API documentation specify different request fields and timeout behavior.

The HTTP request succeeds but the saved file is not an image

Some endpoints return JSON errors or asynchronous job details instead of image bytes. Check the status code and content type before saving the body; follow the provider’s documented job polling or download flow when applicable.

Performance, reliability, and cost

  • Use the narrowest reliable condition. A selector tied to the needed component can avoid waiting for unrelated network activity. Confirm it marks usable content rather than merely a loading shell.
  • Network idle trades simplicity for possible waiting. It can suit request-driven pages, but ongoing analytics, streaming, or polling may prevent settling. Prefer a specific selector if persistent traffic is common.
  • Keep timeouts bounded. A finite navigation and content wait limits the time a slow or stuck page consumes. Choose values based on the page and provider limits, not a universal rule.
  • Retries need judgment. A timeout can be transient, but retrying the same request with the same condition and no limit can multiply latency and usage. Retry selectively and keep an overall deadline.
  • Check billing semantics per provider. Waits can affect latency and whether a capture completes, but billing policies vary. Read the provider’s terms for failed loads, timeouts, retries, and asynchronous jobs.

ScreenshotNeo states that only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and responses include X-Page-Verdict and X-Billed headers. Its plans include all features: Free offers 1,000 shots monthly with no card; Starter is $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. See the docs for implementation details.

FAQ

Should I wait for a selector or network idle?

Use a selector when one stable element marks the content needed in the image. Use network idle when multiple requests jointly populate the view and no dependable selector captures readiness.

Does a fixed delay guarantee that JavaScript finished?

No. It only waits for the chosen duration. It cannot tell whether required content loaded or whether a request failed.

Can I use the same wait parameter across screenshot APIs?

No. Check each provider’s current docs. Similar concepts often have different field names, accepted values, defaults, and limits.

What should I do if the page never becomes network idle?

Use a content-specific selector when possible and set finite timeouts. A page with polling or persistent connections may not reach the provider’s network-idle condition promptly.