A standard viewport screenshot only captures the visible portion of a page, typically 1280×720 or similar. But most web pages extend well beyond the fold. Landing pages, documentation, pricing tables, legal pages -- they can stretch thousands of pixels tall. If you need the entire page in a single image, you need a full-page screenshot.
This guide covers how to capture full-page screenshots using an API, how to handle tricky scenarios like lazy-loaded images and infinite scroll, and complete code examples you can drop into your project today.
A full-page screenshot captures the entire scrollable content of a web page in one image, from the top of the <body> to the very bottom. The resulting image is typically the same width as your chosen viewport but much taller than a standard screen.
Common use cases include:
The fastest way to capture a full-page screenshot is with the GrabShot API. Pass full_page=true and you get the entire page:
curl "https://grabshot.dev/api/screenshot?url=https://example.com&full_page=true&api_key=YOUR_API_KEY" \
--output full-page.png
That's it. The API launches a real Chromium browser, loads the page, scrolls to trigger lazy content, and returns a PNG of the full document height.
You can control the viewport width, format, and quality:
# Full-page screenshot at 1440px wide, JPEG format, 85% quality
curl "https://grabshot.dev/api/screenshot?\
url=https://example.com\
&full_page=true\
&width=1440\
&format=jpeg\
&quality=85\
&api_key=YOUR_API_KEY" \
--output full-page.jpg
The viewport width determines the image width. Height is determined automatically by the page content.
Here's a complete Node.js function that captures a full-page screenshot and saves it to disk:
const fs = require('fs');
async function captureFullPage(url, outputPath, options = {}) {
const params = new URLSearchParams({
url,
full_page: 'true',
width: options.width || '1280',
format: options.format || 'png',
api_key: process.env.GRABSHOT_API_KEY,
});
if (options.quality) params.set('quality', options.quality);
if (options.delay) params.set('delay', options.delay);
const response = await fetch(
`https://grabshot.dev/api/screenshot?${params}`
);
if (!response.ok) {
throw new Error(`Screenshot failed: ${response.status}`);
}
const buffer = Buffer.from(await response.arrayBuffer());
fs.writeFileSync(outputPath, buffer);
return { path: outputPath, size: buffer.length };
}
// Usage
const result = await captureFullPage(
'https://stripe.com/pricing',
'./stripe-pricing-full.png',
{ width: '1440' }
);
console.log(`Saved ${result.size} bytes to ${result.path}`);
The same thing in Python using requests:
import requests
import os
def capture_full_page(url, output_path, width=1280, fmt="png", quality=None):
params = {
"url": url,
"full_page": "true",
"width": width,
"format": fmt,
"api_key": os.environ["GRABSHOT_API_KEY"],
}
if quality:
params["quality"] = quality
resp = requests.get("https://grabshot.dev/api/screenshot", params=params)
resp.raise_for_status()
with open(output_path, "wb") as f:
f.write(resp.content)
return {"path": output_path, "size": len(resp.content)}
# Capture a full-page screenshot of a documentation site
result = capture_full_page(
"https://docs.python.org/3/tutorial/index.html",
"python-docs-full.png",
width=1440
)
print(f"Saved {result['size']} bytes")
Modern websites defer image loading until the user scrolls them into view. This is great for performance, but it means a naive screenshot of the full page will have blank placeholders where images should be.
GrabShot handles this automatically: before capturing a full-page screenshot, the browser scrolls through the entire page to trigger lazy-loaded images, IntersectionObserver callbacks, and scroll-triggered animations. You can also add a delay to give extra time for content to render:
curl "https://grabshot.dev/api/screenshot?\
url=https://example.com/gallery\
&full_page=true\
&delay=2000\
&api_key=YOUR_API_KEY" \
--output gallery-full.png
The delay parameter (in milliseconds) adds a pause after the page load event, giving JavaScript time to fetch and render deferred content.
Pages with infinite scroll (Twitter feeds, Pinterest boards, search results that load on scroll) present a unique challenge: they never truly "end." The page keeps growing as you scroll.
For these pages, you have two practical strategies:
# Capture up to 5000px of an infinite-scroll page
curl "https://grabshot.dev/api/screenshot?\
url=https://example.com/feed\
&full_page=true\
&max_height=5000\
&api_key=YOUR_API_KEY" \
--output feed-partial.png
This prevents the API from scrolling indefinitely and gives you a practical snapshot of the first chunk of content.
One of the most valuable uses of full-page screenshots is visual regression testing. By capturing the complete page before and after a code change, you can detect unintended layout shifts, broken styles, or missing content anywhere on the page -- not just above the fold.
A basic workflow looks like this:
const { execSync } = require('child_process');
// Capture baseline and candidate
async function comparePages(baseUrl, candidateUrl, pagePath) {
const baseline = await captureFullPage(
`${baseUrl}${pagePath}`,
`./screenshots/baseline-${Date.now()}.png`,
{ width: '1440' }
);
const candidate = await captureFullPage(
`${candidateUrl}${pagePath}`,
`./screenshots/candidate-${Date.now()}.png`,
{ width: '1440' }
);
// Use ImageMagick to generate a diff
const diffPath = `./screenshots/diff-${Date.now()}.png`;
execSync(
`compare -metric AE "${baseline.path}" "${candidate.path}" "${diffPath}"`,
{ stdio: 'pipe' }
);
return { baseline, candidate, diff: diffPath };
}
For a more complete solution, check out DiffShot, which handles the comparison, highlighting, and reporting for you.
GrabShot's free plan includes 25 screenshots per month with full-page support. No credit card required.
Try It Now →Full-page screenshots of content-heavy sites can produce large files. A 1440px-wide screenshot of a long page might weigh 5-15 MB as a PNG. Here are some strategies to keep file sizes manageable:
| Strategy | How | Typical Savings |
|---|---|---|
| Use JPEG instead of PNG | format=jpeg&quality=80 | 60-80% smaller |
| Use WebP | format=webp&quality=80 | 70-85% smaller |
| Reduce viewport width | width=1024 | 30-50% smaller |
| Set max height | max_height=3000 | Caps file size |
For archival purposes, PNG is the safest choice (lossless). For sharing on Slack, embedding in reports, or visual comparisons where pixel-perfect accuracy isn't critical, JPEG or WebP will save significant bandwidth.
Need to capture a dashboard or admin panel behind a login? You can pass cookies or custom headers to the API:
curl "https://grabshot.dev/api/screenshot?\
url=https://app.example.com/dashboard\
&full_page=true\
&api_key=YOUR_API_KEY" \
-H "X-Screenshot-Cookie: session_id=abc123; token=xyz789" \
--output dashboard-full.png
This lets you capture internal tools, SaaS dashboards, and gated content without exposing credentials in the URL.
Full-page screenshots aren't always the right tool. Skip them when:
Full-page screenshots let you capture every pixel of a web page in a single image. With the GrabShot API, it's a single parameter: full_page=true. The API handles lazy loading, scrolling, and rendering automatically.
Whether you're archiving legal documents, running visual regression tests, or building design review workflows, full-page capture gives you the complete picture. Try it free and see for yourself.