Fix Screenshot API Captures That Crop the Bottom of a Long Web Page
Fix screenshots that stop at the viewport by checking full-page options, browser limits, and page dimensions. Includes runnable Playwright, Puppeteer, and CDP examples.
If a screenshot stops at the bottom of the visible browser viewport, first check that the capture request asks for the full scrollable page. Playwright and Puppeteer use fullPage: true; at the Chrome DevTools Protocol (CDP) layer, check captureBeyondViewport and any clip region. If the full-page option is already enabled, inspect the browser or provider’s size limits and compare the page’s scrollable height with the image’s actual dimensions. There is no single universal fix for every screenshot API.
This guide shows how to check the capture layer, run the relevant code, diagnose remaining crops, and decide when a full-page capture is too large for the browser implementation.
1. Identify the capture layer and request full-page capture
Before changing code, record the screenshot library and version, browser engine and version, and whether a hosted screenshot API sits between your code and the browser. A wrapper may use a different option name or may not pass the browser option through. Apply the setting at the layer that actually takes the screenshot.
| Capture layer | What to check | Relevant default |
|---|---|---|
| Playwright | Set fullPage: true in page.screenshot(). |
fullPage defaults to false. |
| Puppeteer | Set fullPage: true; inspect clip and captureBeyondViewport too. |
captureBeyondViewport defaults to false without a clip and true with one. |
| CDP | Set captureBeyondViewport: true and review the clip coordinates and dimensions. |
captureBeyondViewport defaults to false. |
| Hosted screenshot API | Check the provider’s option name, browser engine, and maximum output dimensions. | Provider-specific. |
Playwright: runnable Node.js example
Install Playwright and its Chromium browser using the commands in the official Playwright getting-started guide. Save this as full-page.mjs and run it with Node.js. Replace the example URL with the page you need to capture.
import { chromium } from 'playwright';
const browser = await chromium.launch();
try {
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto('https://example.com', { waitUntil: 'load' });
await page.screenshot({ path: 'full-page.png', fullPage: true });
} finally {
await browser.close();
}
Playwright documents fullPage: true as capturing the full scrollable page instead of the visible viewport. See the Playwright screenshot guide and Page API reference.
Puppeteer: runnable Node.js example
Install Puppeteer using the official installation guide. Save this as full-page-puppeteer.mjs and run it with Node.js.
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900 });
await page.goto('https://example.com', { waitUntil: 'load' });
await page.screenshot({ path: 'full-page.png', fullPage: true });
} finally {
await browser.close();
}
Puppeteer documents fullPage as a screenshot of the full page, and documents a captureBeyondViewport option. If you provide a clip, verify that its coordinates and dimensions cover the intended content: a clip deliberately restricts the capture area. See the Puppeteer ScreenshotOptions reference.
CDP: request capture beyond the viewport
If your code calls CDP directly, check the parameters sent to Page.captureScreenshot. A clip restricts the captured region; omit it when you intend to capture without that restriction. This example uses a small Node.js helper that sends a CDP command through a Playwright page session. The browser must be Chromium.
import { chromium } from 'playwright';
const browser = await chromium.launch();
try {
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto('https://example.com', { waitUntil: 'load' });
const session = await page.context().newCDPSession(page);
const result = await session.send('Page.captureScreenshot', {
format: 'png',
captureBeyondViewport: true
});
await import('node:fs/promises').then(({ writeFile }) =>
writeFile('full-page.png', Buffer.from(result.data, 'base64'))
);
} finally {
await browser.close();
}
CDP defines captureBeyondViewport as capture beyond the viewport and gives it a default of false. A clip can still limit what is captured. Refer to the CDP Page.captureScreenshot reference.
2. If the bottom is still missing, measure and inspect
Once the full-page option is set, collect evidence before changing more options. Measure the document’s scrollable height and compare it with the output image height. Also check the response or browser error and verify that the missing content belongs to the main document rather than a separately scrolling element.
const dimensions = await page.evaluate(() => ({
viewportHeight: window.innerHeight,
documentHeight: Math.max(
document.documentElement.scrollHeight,
document.body?.scrollHeight ?? 0
),
documentWidth: Math.max(
document.documentElement.scrollWidth,
document.body?.scrollWidth ?? 0
)
}));
console.log(dimensions);
If the document reports a much greater height than the resulting image, check the selected capture method, browser or provider limits, and any clip. If the document height itself is shorter than expected, investigate whether the content exists in the page at capture time. The available evidence does not establish one universal cause or fix for content that loads late, nested scrolling regions, or fixed and sticky elements; reproduce the issue with a minimal page and the exact browser, library, and provider.
3. Check browser and provider size limits
Full-page capture is not unlimited. A cited Chromium PageHandler implementation checks the page width and height and returns Page is too large. when either reaches 128 × 1024 pixels. That threshold is specific to the cited implementation revision; it is not a general browser standard or a guaranteed limit for every Chromium version, browser, or hosted service. The source comment relates its check to a 16K headless-mode limit, while the check shown is 128 × 1024 pixels. See the Chromium PageHandler source.
When a hosted service crops or rejects a tall page, consult that provider’s documented maximum dimensions and the actual response. Do not assume the service uses the same Chromium code path or threshold. If the browser or provider confirms that the requested image exceeds its limit, use a supported smaller output dimension or capture separate regions and assemble them only if your use case permits it. Check for seams, repeated sticky elements, and gaps before treating stitched output as a faithful page image.
4. Troubleshooting checklist
| Symptom | Likely check | Next step |
|---|---|---|
| Image ends at the viewport bottom | Full-page capture may be off; Playwright and CDP default the relevant setting to false. | Set Playwright or Puppeteer fullPage: true, or CDP captureBeyondViewport: true. |
| Only a section or rectangle appears | A clip or provider crop may restrict the output. |
Inspect clip origin, width, and height; remove it if you need the unrestricted capture. |
Browser reports Page is too large. |
The cited Chromium implementation rejects dimensions at its size check. | Confirm the browser revision and dimensions, then use that implementation’s supported capture size or provider limits. |
| Full-page setting is enabled, but output remains short | A wrapper may not forward the option, or the provider may impose its own behavior or limit. | Log the outgoing options; check library and browser versions and provider documentation. |
| Expected content is absent from the document measurement | The content may not be present in the page at capture time. | Inspect the page and capture timing; create a minimal reproduction before attributing the crop to the screenshot API. |
| Content belongs to a separately scrolling panel | Document full-page capture concerns the document, not necessarily an inner scrolling element. | Identify the element that owns the content and determine whether your capture method can capture that element. |
| Unexpected crop from a hosted API | Provider-specific parameters, dimensions, or response behavior may apply. | Check the provider’s documentation and inspect its returned dimensions and error details. |
5. Reliability, performance, and cost considerations
- Reliability: Record the browser engine and version, automation library and version, request options, page dimensions, output dimensions, and any provider response. This makes it easier to distinguish an option that was not applied from a size rejection or page-specific behavior.
- Performance: A full-page image contains more pixels than a viewport capture, so its output can take more resources to produce and store. The sources cited here do not establish a timing or memory benchmark. Measure your own representative pages and image formats.
- Size: Width as well as height can matter to an implementation’s dimension check. Capture the actual output dimensions and check provider limits instead of inferring them from CSS height alone.
- Cost: For a hosted API, confirm how it bills successful captures, failures, and retries. The research for this browser troubleshooting guide does not establish pricing for other providers.
- Reproduction: Keep one failing URL or a minimal local page, the exact request, and the actual response. A title alone cannot identify which layer is cropping the image.
Or skip the browser setup
ScreenshotNeo is a website screenshot API: send one GET request with a URL and receive an image or PDF. Its full-page option loads lazy images. Cookie and consent banners are accepted like a visitor, and more than 60 known consent platforms, newsletter popups, and chat widgets are removed before the shot; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the page verdict and billing status in headers.
Use the ScreenshotNeo API documentation for the request options and output formats. Put your API key in an environment variable before running the examples.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key="$SCREENSHOTNEO_API_KEY" \
--data-urlencode url=https://example.com \
-d full_page=true \
-o shot.webp
Python
import os
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": os.environ["SCREENSHOTNEO_API_KEY"],
"url": "https://example.com",
"full_page": "true",
},
timeout=90,
)
r.raise_for_status()
with open("shot.webp", "wb") as f:
f.write(r.content)
Node.js
const q = new URLSearchParams({
access_key: process.env.SCREENSHOTNEO_API_KEY,
url: 'https://example.com',
full_page: 'true'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(async ({ writeFile }) =>
writeFile('shot.webp', Buffer.from(await res.arrayBuffer()))
);
The response can identify whether a page was cleanly captured, blocked by a bot check, blank, or failed to load, and whether it was billed. ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for AI agents. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots.
Sign up for 1,000 free screenshots a month, with no card required.
FAQ
Why is my full-page screenshot cut off at the bottom?
First check whether the request actually enables full-page capture at the layer taking the screenshot. If it does, compare document and output dimensions and inspect clips, browser limits, and provider behavior.
Does a viewport screenshot include everything below the fold?
No. A viewport screenshot captures the visible area. Request full-page capture when you need the scrollable document.
Is Chromium’s 128 × 1024-pixel check a universal limit?
No. It is an implementation finding from a cited Chromium source revision. Check the exact browser build or provider documentation for the limit that applies to your request.
Will full-page capture include every scrolling panel?
Do not assume that it will. Confirm whether the missing content belongs to the main document or to an independently scrolling element, then test the capture method against that structure.


