August 12, 2026

Full-page screenshots of lazy-loaded pages

Why full_page captures on long, image-heavy pages come back with blank gaps — and the three parameters that fix it.

Set full_page=true on a long page and you'll sometimes get back a screenshot with visible gaps — white or gray bands where an image, a chart, or a below-the-fold section should be. The page looks fine in a real browser. The capture doesn't match it.

Why this happens

Most modern sites lazy-load anything below the fold: images get a real src only once an IntersectionObserver decides they're about to enter the viewport, and some frameworks defer whole sections the same way. A headless browser capturing full_page renders the page at its full scrolled height in one pass — nothing ever "scrolls past" the lazy content to trigger it, so those elements are still in their unloaded placeholder state when the screenshot is taken.

The fix isn't a single flag. It's picking the right combination of three parameters, depending on what the page is doing.

wait_until — pick the right definition of "loaded"

wait_until=load (the default) resolves as soon as the initial HTML, CSS, and images referenced in the markup have loaded — before most JavaScript-driven lazy loading has had a chance to run. wait_until=networkidle waits until the page has gone quiet on the network for a short stretch, which is usually enough for lazy-loaded images to have fired their requests. Start here:

&full_page=true&wait_until=networkidle

For pages where "quiet" never quite arrives — infinite scroll feeds, sites with a background polling request — networkidle can wait longer than you want, or your lazy content still hasn't loaded because nothing ever scrolled past it. That's what the other two parameters are for.

wait_for — wait for proof the content is there

wait_for takes a CSS selector and holds the capture until that element exists in the DOM. If you know the last thing on the page that should render — a footer, a "load more" button, the final card in a grid — target it directly:

&full_page=true&wait_for=footer.site-footer

This is the most reliable option when it's available, because it's the one that verifies the actual outcome instead of guessing at a timing window.

delay — the blunt instrument, used deliberately

Sometimes there's no reliable selector to wait for, or the lazy-loading library needs a scroll event that a static full-page render never generates. delay adds a fixed pause, in milliseconds, after whatever wait_until/wait_for condition resolves — up to 5000ms:

&full_page=true&wait_until=networkidle&delay=1500

Reach for delay last. It's the least precise of the three — you're paying for a fixed wait whether the page needed it or not — but it's the one guaranteed to work when a page's lazy-loading doesn't expose anything cleaner to hook into.

A reasonable default

For most image-heavy marketing pages, wait_until=networkidle combined with a short delay covers the common case without needing to know the page's markup in advance:

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

If you're capturing the same page repeatedly, it's worth spending five minutes finding a wait_for selector instead — it's faster and it doesn't silently break the day the page gets slower.

See the full parameter reference for everything else /v1/screenshot accepts.

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

Open the playground Read the docs