How to Capture a Page After It Loads with ScreenshotAPI
Use ScreenshotAPI’s readiness events, selector waits, and post-load delay to capture dynamic pages reliably. Includes runnable cURL, Python, and Node.js examples.
To capture a page after it loads with ScreenshotAPI, set waitUntil to the browser event you need, then add waitForSelector when a specific piece of content must appear before capture. Use delayMs only for a known extra settling period, and set timeoutMs to bound navigation time. These are separate controls: a navigation event does not guarantee an application-specific widget is ready.
ScreenshotAPI’s documented endpoint accepts a POST request with JSON and a bearer API key. Its documented waitUntil values are load, domcontentloaded, networkidle0, and networkidle2; the default is networkidle2. See the ScreenshotAPI API reference for current request details.
Choose the right readiness condition
| Option | What it waits for | Use it when |
|---|---|---|
domcontentloaded |
The initial document has been parsed, without waiting for every dependent resource. | You need an early capture and the relevant content is available with the document. |
load |
The page’s load event, after dependent resources such as images and stylesheets have loaded. | The page is mostly static and those resources affect the appearance. |
networkidle0 |
The network has no active connections, according to the service’s browser readiness option. | The page settles its requests and you need a quieter network state. |
networkidle2 |
The network is idle with up to two active connections; this is the documented default. | You want the default behavior or the page keeps a small number of connections open. |
These events are general navigation signals. Analytics, polling, streaming, and other long-lived requests can make network-idle strategies a poor fit. Conversely, a page can reach a navigation event before client-side content appears. When you know what content matters, a selector wait gives the request a page-specific condition.
Send a request that waits for page content
Replace YOUR_API_KEY with your ScreenshotAPI key and .main-content with a CSS selector that appears when the content you need is present. The 500 ms delay below is illustrative: remove it if the selector is enough, or adjust it only when the page needs a known short settling period.
cURL
curl -X POST "https://api.screenshot-api.org/api/v1/screenshot" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"format": "png",
"waitUntil": "networkidle2",
"waitForSelector": ".main-content",
"delayMs": 500,
"timeoutMs": 30000
}' --output page.png
Python
import requests
api_key = "YOUR_API_KEY"
payload = {
"url": "https://example.com",
"format": "png",
"waitUntil": "networkidle2",
"waitForSelector": ".main-content",
"delayMs": 500,
"timeoutMs": 30000,
}
response = requests.post(
"https://api.screenshot-api.org/api/v1/screenshot",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json=payload,
timeout=45, # Client-side deadline; keep this longer than the API navigation timeout.
)
response.raise_for_status()
with open("page.png", "wb") as image_file:
image_file.write(response.content)
Node.js
const apiKey = 'YOUR_API_KEY';
const payload = {
url: 'https://example.com',
format: 'png',
waitUntil: 'networkidle2',
waitForSelector: '.main-content',
delayMs: 500,
timeoutMs: 30000,
};
const response = await fetch('https://api.screenshot-api.org/api/v1/screenshot', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
signal: AbortSignal.timeout(45000),
});
if (!response.ok) {
throw new Error(`ScreenshotAPI request failed: ${response.status} ${await response.text()}`);
}
const image = Buffer.from(await response.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('page.png', image));
The service’s documented navigation timeout default is 30,000 ms. The Python and Node.js client timeouts shown above are illustrative client-side limits, set longer so the client does not give up before the API’s navigation deadline and response transfer. Keep credentials in environment variables or a secret manager in deployed applications; do not put API keys in browser code, public repositories, or logs.
Configure waits for the page you have
- Start with a readiness event. Omit
waitUntilto use the documentednetworkidle2default, or choose another listed event based on how the page renders. - Wait for a meaningful selector. Use
waitForSelectorfor a stable CSS selector that indicates the target data or component is present, such as.article-bodyor[data-testid="results"]. Avoid selectors tied to fragile styling classes when the page offers stable attributes. - Add delay only for a known late update.
delayMsis a fixed pause after load in milliseconds. It does not check whether a particular element appeared, so it can waste time or still capture too early. - Set a navigation deadline.
timeoutMslimits navigation time; it is not the post-load pause. Its documented default is 30,000 ms.
You can combine the controls: use a navigation event to establish general readiness, a selector to wait for the important content, and a short delay if a known animation or delayed visual update follows. Avoid stacking long waits without a reason, because each can extend request time.
Handle common page-loading edge cases
- Single-page applications: the initial document can load before route data renders. Wait for a selector belonging to the rendered view.
- Continuously active pages: polling or streaming may prevent a network-idle condition from being reached. Try a selector wait with a suitable navigation event, and retain a finite timeout.
- Lazy-loaded content: content below the fold may not be requested until scrolling. A readiness event alone may not trigger it. Confirm the target page’s behavior and use a capture workflow that causes the content to load if available in the service’s current options.
- Selector never appears: check that the selector exists in the rendered DOM for the requested URL, including any consent or authentication state. A selector wait cannot succeed for content hidden behind a required login.
- Variable render times: prefer waiting for the content condition over choosing a large fixed delay based on the slowest observed page.
- Redirects and blocked destinations: verify the final destination is reachable by the screenshot service and that the page does not require an interactive login or human-only challenge.
Troubleshooting
| Symptom | Likely cause | What to try |
|---|---|---|
| Screenshot shows a spinner or empty app shell | The navigation event occurred before client-side rendering completed. | Add waitForSelector for the loaded content, and confirm the selector matches the rendered page. |
| Request times out during navigation | The destination is slow, unreachable, or waiting for the selected network condition indefinitely. | Check the URL and reachability; choose a more suitable waitUntil value and set a realistic timeoutMs. |
| Screenshot is still stale after adding a delay | The update takes longer than the chosen pause or the delay starts before the relevant app state is ready. | Use a selector for the content state, then use only a small additional delay if necessary. |
| Selector wait does not complete | The selector is misspelled, absent on this route, or only created after an interaction. | Inspect the page’s DOM and choose a selector that exists without unsupported interactions. |
| Response is an error instead of an image | The key, request body, URL, or service-side capture failed. | Check the HTTP status and response body; verify bearer authentication, valid JSON, and a publicly reachable URL. |
| Local client times out first | The client-side deadline is shorter than navigation plus capture and transfer time. | Increase the client timeout beyond timeoutMs with room for response delivery. |
Performance, reliability, and cost considerations
Capture time includes navigation, the chosen readiness condition, any selector wait or delay, rendering, and response transfer. Choose the earliest condition that still captures the content you need. Selector-based waits can avoid arbitrary extra pauses; large fixed delays and strict network-idle requirements can add latency or fail on pages with ongoing requests.
For repeated captures, set explicit finite deadlines and handle non-success HTTP responses. Retry only transient failures, with a capped retry count and backoff; repeating an invalid selector or unauthorized request will not fix it. The dossier does not establish ScreenshotAPI pricing or billing behavior, so check its current official pricing and terms before estimating cost.
Or skip the browser setup
ScreenshotNeo captures a page with one GET request and supports wait conditions, selector waits, delay, and other capture options. For example:
ScreenshotNeo API documentation
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; each step can be turned off.
- Bot checks, blank pages, and failed loads are never billed; response headers identify the page verdict and billing status.
- An MCP server lets AI agents, including Claude and Cursor, take screenshots.
- 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000.
Sign up free and capture 1,000 screenshots a month with no card.
FAQ
Is waitUntil enough for a JavaScript-rendered page?
Not always. It waits for a browser navigation event, while an application can render important content afterward. Add a selector for that content when possible.
Should I use a selector wait or a delay?
Use a selector when the desired element is identifiable. Use a delay for a known short settling period that has no reliable element condition.
Does timeoutMs include delayMs?
The reference describes timeoutMs as the navigation timeout and delayMs as an extra post-load pause. Treat them as distinct controls and allow your client deadline enough time for both capture work and response delivery.
What is the default readiness event?
The ScreenshotAPI reference documents networkidle2 as the default.


