August 16, 2026

wait_until vs wait_for vs delay: getting a page to hold still

Three ways to tell the screenshot API a page is ready to capture, what each one actually checks, and when to reach for which.

/v1/screenshot and /v1/extract both take three separate parameters for controlling when a page is considered "ready." They solve different problems, and mixing them up is the most common reason a capture comes back either too early (missing content) or slower than it needs to be.

wait_until: a navigation-level signal

wait_until answers "has the page finished navigating," not "has the page finished rendering everything I care about." Three values:

  • load (default) — the load event has fired: the document and its initial subresources (images referenced directly in HTML, stylesheets, etc.) have loaded. Fastest, and sufficient for most static or server-rendered pages.
  • domcontentloaded — resolves even earlier, once the HTML is parsed, before images and other subresources necessarily finish. Rarely what you want for a visual capture; useful mainly for /v1/extract against text-heavy pages where you don't need images loaded at all.
  • networkidle — waits until the page has been quiet on the network for a short window. Closer to "a human would consider this loaded," and usually the right choice for JavaScript-heavy pages that fetch data or lazy-load content after the initial load event.

wait_until is a blunt, page-wide signal. It doesn't know anything about your page's specific content — it's watching network activity, not your DOM.

wait_for: a content-level signal

wait_for takes a CSS selector and blocks the capture until a matching element exists in the DOM. This is the sharpest tool of the three, because it checks the thing you actually care about instead of a proxy for it:

&wait_for=.dashboard-chart[data-loaded="true"]

Use it whenever there's a reliable selector for "the thing I'm trying to capture is actually there" — a chart library that adds a class once it's done drawing, a price that replaces a loading skeleton, the last card in a paginated list. It has no fixed timeout cost: if the selector appears in 200ms, the capture proceeds in 200ms.

The tradeoff is that it requires knowing the page's markup, and it breaks (with a 408 selector_timeout) if that markup changes or the selector never appears.

delay: a fixed pause, no signal at all

delay (0–5000ms) adds a flat wait on top of whatever wait_until/wait_for condition already resolved. It doesn't check anything — it's not "wait until X," it's "then also just wait."

Reach for delay when there's no reliable selector to target and networkidle doesn't fully cover it — a CSS transition that finishes after the network has already gone quiet, or a canvas/WebGL render that doesn't touch the DOM in a way wait_for can see.

Combining them

The three stack, and most non-trivial pages need at least two:

curl "https://snapurl.app/v1/screenshot?url=https://example.com&wait_until=networkidle&wait_for=.hero-loaded&delay=300" \
  -H "Authorization: Bearer $SNAPURL_KEY" \
  -o shot.png

Order of preference, from most to least precise: a wait_for selector, then wait_until=networkidle, then delay as the fallback for whatever the first two can't see. All three are documented in full in the API reference, including how they interact with full-page captures on lazy-loaded pages.

Try it against a real page — no sign-up required.

Open the playground Read the docs