Capturing Entire Webpages: What a Full Page Screenshot API Actually Does
How a full page screenshot API handles lazy loading, sticky headers, and infinite scroll — with code examples for production use.
Taking a screenshot of what's visible in the viewport is easy. Capturing an entire 12,000-pixel marketing page with lazy-loaded images, sticky navigation, cookie banners, and a footer that animates in on scroll — that's where most homegrown solutions fall apart. A dedicated full page screenshot API exists precisely because rendering a complete page reliably is harder than it looks.
This post breaks down what actually happens during a full-page capture, the edge cases that trip up DIY scripts, and how to wire it into a real workflow.
What "Full Page" Actually Means
When you request a full-page screenshot, the renderer needs to:
- Load the page in a headless browser at a chosen viewport width
- Wait for network activity and JavaScript to settle
- Measure the full document height (which can grow as content loads)
- Scroll progressively to trigger lazy-loaded images and intersection observers
- Resolve sticky and fixed elements so they don't repeat down the image
- Stitch the final output into a single PNG, JPEG, WebP, or PDF
Skip any of these steps and you get the typical broken output: gray image placeholders, a navigation bar duplicated three times down the page, or a screenshot cut off at 6,000 pixels because the height was measured too early.
The Edge Cases That Break Naive Implementations
Lazy-loaded images
Most modern sites use loading="lazy" or IntersectionObserver to defer images. If you call page.screenshot({ fullPage: true }) in Puppeteer without scrolling first, you'll capture the page with most images still unloaded. A proper full page screenshot API scrolls the viewport in steps, waits for new network requests to complete, then resets scroll position before capturing.
Sticky headers
A position: sticky or position: fixed header will appear at every scroll position during capture, producing a screenshot with the header stamped repeatedly. The fix is to inject CSS that converts sticky/fixed elements to position: absolute at the top of the document before capture.
Infinite scroll
Pages with infinite scroll have no natural "bottom." You need a configurable scroll limit or a content-stable detector that stops when the document height hasn't changed for N scroll cycles.
Cookie banners and modals
These overlay your screenshot and ruin OG image generation. Solutions include CSS injection to hide common selectors, a cookie-consent extension, or passing pre-set cookies to bypass the prompt entirely.
Web fonts
Capturing before fonts load gives you fallback typography or invisible text (FOIT). Wait for document.fonts.ready before taking the shot.
A Minimal Working Example
Here's a typical request to PxShot to capture a full marketing page as PNG:
GET https://api.pxshot.dev/v1/screenshot
?url=https://example.com/pricing
&full_page=true
&format=png
&width=1440
&block_cookies=trueAnd the same thing in Node:
const res = await fetch('https://api.pxshot.dev/v1/screenshot?' + new URLSearchParams({
url: 'https://example.com/pricing',
full_page: 'true',
format: 'webp',
width: '1440'
}), {
headers: { 'Authorization': 'Bearer ' + process.env.PXSHOT_KEY }
});
const buffer = Buffer.from(await res.arrayBuffer());
await fs.writeFile('pricing.webp', buffer);That's it — no headless Chromium in your container, no memory leaks from unclosed browser instances, no debugging why your Lambda runs out of memory on pages over 8,000 pixels tall.
Common Use Cases Worth Building
- Automated OG images — render a templated page on your own domain, screenshot it, cache the result. No Canvas or Satori needed.
- Visual regression checks — capture the same URL on staging and production, diff with pixelmatch or Resemble.js.
- Compliance archives — full-page captures of terms pages, marketing claims, or third-party listings, timestamped for legal record.
- PDF generation from HTML — request
format=pdfinstead of an image and you get a print-ready document. - Competitor monitoring — daily full-page snapshots of competitor landing pages, stored in S3 with a date prefix.
- Link previews — for chat apps, internal tools, or bookmarking apps that need a thumbnail of any URL.
Performance Considerations
Image format matters
A 1440×8000 PNG can easily hit 4 MB. The same screenshot as WebP at quality 85 is often under 600 KB with no visible quality loss. For OG images, JPEG at 80 quality is usually the sweet spot since Twitter, LinkedIn, and Slack will recompress anyway.
Caching
Most pages don't change minute-to-minute. Cache screenshots in a CDN or object store keyed by URL + viewport + format. A simple SHA-256 of those inputs makes a good cache key. PxShot returns proper Cache-Control headers so a CDN in front of it does most of the work for you.
Concurrency
Running headless Chrome yourself means you'll hit memory and CPU ceilings quickly — a single Chromium instance can use 300–500 MB before rendering anything. Offloading to an API trades infrastructure complexity for predictable per-request pricing.
What to Look for When Choosing One
- Configurable viewport width and device scale — needed for both mobile captures and retina-quality OG images
- Custom wait conditions — wait for a selector, network idle, or a fixed delay
- CSS and JavaScript injection — to hide chat widgets, accept cookies, or expand collapsed sections
- Authenticated captures — cookies, headers, or basic auth for screenshots behind a login
- Multiple output formats — PNG for transparency, JPEG for size, WebP for both, PDF for documents
- Predictable latency — sub-3-second response times for cached pages, sub-10-second for cold renders
- Honest pricing — per-screenshot or per-1000, with a free tier you can actually build against
Try It on a Real Page
The fastest way to know if a service handles your specific edge cases is to throw your worst page at it — the long one with the carousel, the cookie banner, and the animated footer. PxShot's free tier gives you enough monthly screenshots to validate it against your actual URLs before you commit to anything. Grab a key at pxshot.dev and run your first full-page capture in about two minutes.