ScreenshotNeo

BlogHow-to

Screenshot API Times Out on a Slow Website: How to Fix It

Find which timeout is failing, choose a reliable page-ready signal, and budget navigation and screenshot waits without blindly extending every deadline.

By the ScreenshotNeo team4 October 20269 min read

A screenshot request can time out at several different stages: connecting to the screenshot API, navigating to the target page, waiting for content, capturing the image, or exceeding the overall request deadline. First identify which boundary failed. Then wait for the least restrictive navigation milestone that produces a usable page and add a selector or application-specific readiness condition for the content that must appear in the screenshot.

Making every timeout larger is rarely the right first fix. A page can finish its initial navigation before its client-rendered content appears, while a persistent analytics request can prevent a network-idle condition indefinitely. Diagnose the failing stage, then adjust only the relevant wait.

1. Identify the timeout boundary

Record the exact error and elapsed time. Compare them with the configured connection timeout, navigation timeout, selector or function wait, screenshot operation timeout, and total request deadline. These may be separate limits. Changing the navigation timeout will not help if the outer API request deadline expires first.

Stage Typical clue What to inspect
Client to screenshot API No browser or page details; client reports connect or read timeout Network path, DNS, TLS, client timeout, API availability and authentication
Navigation Timeout names URL navigation or goto URL reachability, redirects, navigation milestone, browser logs
Content readiness Selector or function wait expires Whether the selector exists, is in the right frame, and becomes visible
Screenshot operation Capture itself exceeds its configured limit Page size, full-page capture, resource load, provider operation limit
Whole request Client or provider deadline ends while inner operation is still running Outer deadline versus the sum of navigation, readiness, and capture time

For example, Browserless documents a query-level timeout that covers the full operation and its waits, alongside body-level navigation and selector waits. Its documentation’s 60-second total, 30-second navigation, 10-second selector, and 2-second delay values are illustrative settings, not universal recommendations. The outer budget must leave room for inner waits and image generation. See the [Browserless timeout documentation](https://docs.browserless.io/baas/features/screenshot).

Authentication failures are different from slow navigation. Browserless identifies a missing or invalid token as a cause of HTTP 401; resolve that before changing wait settings.

2. Choose a navigation milestone that fits the capture

Navigation completion and page readiness are different events. Playwright supports these waitUntil milestones:

Milestone What it means When it can help
commit The response was received and document loading has started. When you will wait explicitly for the page content afterward.
domcontentloaded The initial HTML was parsed and the DOM content event fired. When the capture needs the document structure, followed by a readiness check.
load The load event fired after dependent resources completed according to browser event semantics. When load-event completion is a useful baseline for the page.
networkidle No network connections for at least 500 ms. Only when network quiet is meaningful for the particular page.

Playwright discourages treating networkidle as a general readiness test. Live feeds, analytics, polling, streaming, and long-running requests can make it too strict; conversely, a quiet network does not prove that the specific content you need has rendered. Prefer an assertion or app-specific signal. Refer to [Playwright’s navigation documentation](https://playwright.dev/docs/api/class-page#page-goto).

Puppeteer’s screenshot guide shows navigation with networkidle2 before capture. It is an example, not a requirement for every site: choose the condition that matches the page and your screenshot’s needs. See the [Puppeteer screenshot guide](https://pptr.dev/guides/screenshots).

3. Wait for the content that matters

For a dynamically rendered page, identify a stable element that appears when the useful content is ready: a report heading, main content container, chart, or explicit loaded-state element. Wait for it after navigation, with its own timeout. If the site offers an application event or a reliable response that marks completion, that can be a better signal than a generic network condition.

Use a fixed delay only when there is no reliable observable readiness signal and its tradeoff is acceptable. A longer delay adds latency to every request and still cannot guarantee readiness when a response takes longer than expected. Playwright recommends locator actions and assertions that wait for conditions over production sleeps. Browserless also documents fixed-delay waits as an option, as well as selector and function/event waits. See [Playwright’s best practices](https://playwright.dev/docs/best-practices) and [Browserless screenshot options](https://docs.browserless.io/baas/features/screenshot).

4. Runnable example: Playwright with Python

This example waits for the initial DOM, then for a page-specific selector. The navigation and selector timeouts are separate from any external API or job deadline imposed by your environment.

import asyncio
from pathlib import Path
from playwright.async_api import async_playwright

async def main():
    url = "https://example.com/report"
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page = await browser.new_page(viewport={"width": 1440, "height": 1000})
        try:
            response = await page.goto(
                url,
                wait_until="domcontentloaded",
                timeout=30_000,
            )
            if response is not None and response.status >= 400:
                raise RuntimeError(f"Navigation returned HTTP {response.status}")

            await page.locator("main h1").wait_for(
                state="visible",
                timeout=20_000,
            )
            await page.screenshot(path="shot.png", full_page=True)
        finally:
            await browser.close()

asyncio.run(main())

Install the Python package and browser runtime using the [Playwright Python installation instructions](https://playwright.dev/python/docs/intro). Replace main h1 with a selector that reliably indicates the content is ready on your target page. If the page has multiple frames, locate the element in the correct frame.

5. Runnable example: Playwright with Node.js

import { chromium } from 'playwright';

const url = 'https://example.com/report';
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });

try {
  const response = await page.goto(url, {
    waitUntil: 'domcontentloaded',
    timeout: 30_000,
  });
  if (response && response.status() >= 400) {
    throw new Error(`Navigation returned HTTP ${response.status()}`);
  }

  await page.locator('main h1').waitFor({ state: 'visible', timeout: 20_000 });
  await page.screenshot({ path: 'shot.png', fullPage: true });
} finally {
  await browser.close();
}

The sample timeout numbers are starting configuration choices, not guarantees that a particular site will load within those durations. Match them to your measured request behavior and the overall deadline.

6. Budget the whole operation

Set the outer deadline to include connection setup, navigation, content readiness, screenshot generation, and response transfer. Make it larger than the longest valid combination of inner waits, with reasonable headroom for capture. If a platform enforces its own total cap, your client timeout cannot extend that cap.

Browserless BrowserQL documents a 30-second default for its screenshot operation’s timeout. That value applies to the documented BrowserQL operation; do not assume it applies to other providers, endpoints, or plans. Check the current documentation for the exact API and version you call. See [Browserless BrowserQL screenshot documentation](https://docs.browserless.io/browserql/bql-schema/operations/screenshot).

7. cURL example for Browserless

Provider endpoints and payloads differ. Browserless marks its BaaS v1 /screenshot documentation deprecated and points readers to BaaS v2 or BrowserQL. Do not copy a legacy request into a new integration without checking the version you use. See [Browserless BaaS v1 status and migration links](https://docs.browserless.io/baas/features/screenshot).

The following shape illustrates a request using Browserless’s documented BaaS v1 screenshot endpoint and body-level waits. It is for legacy integrations only; use the current endpoint and schema from Browserless’s documentation for a new integration.

curl -X POST 'https://production-sfo.browserless.io/screenshot?token=YOUR_TOKEN&timeout=60000' \
  -H 'Content-Type: application/json' \
  --data '{
    "url": "https://example.com/report",
    "options": { "fullPage": true },
    "gotoOptions": { "waitUntil": "domcontentloaded", "timeout": 30000 },
    "waitForSelector": { "selector": "main h1", "timeout": 20000 }
  }' \
  --output shot.png

The endpoint, query parameters, and JSON schema are provider- and API-version-specific. If a request fails validation, check the current endpoint documentation before changing timeout values. The outer timeout needs to cover navigation, selector wait, and capture.

8. ScreenshotNeo one-call option

If the issue is maintaining a browser setup or tuning a hosted capture request, [ScreenshotNeo](https://screenshotneo.com) provides a screenshot API: send one GET request with a URL to receive a PNG, JPEG, WebP, or PDF. Its [API documentation](https://screenshotneo.com/docs/) lists 63 options, including selector and delay waits, custom headers and cookies, resource blocking, caching, and async jobs. Check the documentation for supported parameter names and limits for your use case.

cURL

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

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com/report"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/report' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status} ${await res.text()}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

Cookie banners are accepted like a visitor and removed along with 60+ known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and response headers say the page verdict and whether it was billed. An MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots. [Create a free account](https://screenshotneo.com/account/sign-up/) to try it.

9. Troubleshooting checklist

Symptom Likely cause Fix
Navigation times out although the page eventually appears in a normal browser Chosen navigation milestone waits for resources or network quiet the page does not reach promptly Use domcontentloaded or commit when suitable, then wait for the target content explicitly.
Navigation succeeds but screenshot is blank or incomplete Client rendering, lazy loading, or data fetching continues after navigation Wait for a stable visible selector or app-specific ready signal; scroll or use full-page capture when content is lazy-loaded.
Network-idle wait never completes Polling, analytics, streaming, or persistent requests keep the network active Use a selector or application signal instead of requiring global network quiet.
Selector wait times out Selector is wrong, content is in a frame, element never appears, or visibility is not reached Inspect the rendered DOM and frame tree; wait for the correct selector and state; distinguish attached from visible.
Client reports timeout while browser work continues Client read timeout or overall API deadline is shorter than inner waits Align the client deadline and provider request limit with navigation, readiness, and capture budgets.
401 response Missing or invalid credentials Correct the token or API key; this is an authentication error, not evidence of a slow page.
Request rejected before navigation Wrong endpoint generation or payload schema Check the current provider API version and request format; Browserless BaaS v1 screenshot docs are deprecated.
Intermittent failures only on some targets Different redirects, bot checks, geographic behavior, page weight, or third-party dependencies Log final URL, status, elapsed stage times, and browser console/network failures; compare successful and failed runs.

10. Performance, reliability, and cost

  • Use the earliest useful milestone. Waiting for every resource can add delay without improving the captured content.
  • Wait narrowly. A meaningful selector avoids coupling readiness to unrelated analytics or advertising requests.
  • Keep budgets observable. Log stage durations and the final error class so recurring bottlenecks can be separated from occasional slow targets.
  • Bound retries. Retry only transient connection or provider failures, with a limit and backoff. Repeating a request that deterministically waits for a nonexistent selector adds load and latency.
  • Consider capture size. Full-page and high-resolution captures can require more rendering and output work than a viewport image. Request only the area and resolution you need.
  • Account for provider semantics. A managed API may have its own operation timeout and total request cap. Browserless documents hosted browser connections and screenshot APIs, but this research does not establish its pricing or affiliate terms.
  • Estimate API cost from billable outcomes. Confirm which results a provider bills and how cache hits are treated before scaling. ScreenshotNeo states that failed loads, timeouts, bot checks, blank pages, and cache hits are not billed; check its docs for current plan and option details.

11. Frequently asked questions

Should I always use networkidle for screenshots?

No. It describes a network quiet interval, not application readiness. Use it only when quiet network activity corresponds to the content being ready on that page.

Will increasing the timeout fix a blank screenshot?

Only if the content appears after the current deadline. If the capture happens before the relevant component renders, add a wait for that component; if it never appears, inspect page errors and selector correctness.

What if the site has no stable selector?

Look for another observable condition, such as a response, application event, or known state change. A bounded delay is a fallback when no reliable signal exists, with the understanding that it cannot guarantee readiness.

Are Browserless timeout examples safe to copy?

Treat them as configuration examples. Confirm the API generation, endpoint, schema, and current limits for your integration; the cited BaaS v1 screenshot guide is deprecated.

Sources