BlogScreenshots on your device
How to Capture a Website Screenshot with Headless Chrome on macOS
Capture a website from the macOS terminal with Headless Chrome. Set the viewport, choose an output path, and understand when you need a full-page capture.
To capture a website from macOS without opening a visible browser window, run Chrome’s executable with --headless and --screenshot:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--headless \
--screenshot \
"https://example.com/"
Chrome saves screenshot.png in the command’s current working directory. Run the command from the folder where you want the image, or use the output-path form below. Add --window-size=WIDTH,HEIGHT to set the viewport. That controls the visible browser area; it does not guarantee a screenshot of the entire document. These flags and the default output behavior are documented in the Chrome Headless command-line reference.
1. Capture a screenshot from Terminal
- Install Google Chrome for macOS if it is not already installed.
- Open Terminal and move to the folder where you want the screenshot, for example
cd ~/Desktop. - Run the command with the target page URL, including
https://. - Look for
screenshot.pngin that folder.
The standard Chrome application bundle uses this executable path: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome. It is Google’s documented macOS example, but a custom installation or another Chrome channel may use a different path. If the command says the file does not exist, locate that installation’s application-bundle executable and substitute its path.
2. Set the viewport size and output filename
Choose dimensions in CSS pixels for the layout you want Chrome to render. For example, this captures a 1280-by-1696 viewport and writes to a chosen filename:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--headless \
--screenshot="landing-page.png" \
--window-size=1280,1696 \
"https://example.com/"
Chrome’s reference also shows --window-size=412,892 for a narrow, mobile-like viewport. The dimensions change responsive layout by changing the viewport; they do not emulate every device property, and they do not make the capture full-page. If you need the entire scrollable document, use a capture method that explicitly supports full-page screenshots.
Keep the output filename’s extension aligned with the image format you intend to use. The documented default is PNG. Chrome’s command-line screenshot documentation describes saving screenshot.png; do not assume changing the extension converts the image to another format.
3. Allow time for page loading
For pages that need extra time to render, pass a maximum wait in milliseconds:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--headless \
--screenshot="page.png" \
--window-size=1280,900 \
--timeout=5000 \
"https://example.com/"
--timeout=5000 allows up to five seconds before capture. It is a maximum wait, not a signal that the page is fully ready: Chrome can capture even while loading continues. A fixed delay may still miss content that appears after asynchronous requests, user interaction, lazy loading, or a longer-running script. The documented option applies to screenshots, DOM dumps, and PDF output; see the official flag reference.
4. Understand viewport versus full-page capture
A screenshot is often one of two things:
- Viewport capture: the visible area at the selected width and height. This is what the documented
--window-sizeexample establishes. - Full-page capture: an image extended to include content beyond the initial viewport, including the rest of the document’s scrollable height.
Increasing --window-size increases the viewport, but the command-line documentation does not establish it as a reliable full-page capture feature for arbitrary sites. Very tall viewports may also change responsive behavior and produce unwieldy images. For reproducible whole-page output, use a browser automation method or screenshot service with an explicit full-page option.
5. Run it from a script
For one-off captures, Terminal is enough. If you automate this command from a shell script, quote paths and URLs so spaces and shell metacharacters do not get split into separate arguments:
#!/bin/sh
set -eu
CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
URL="https://example.com/"
OUT="page.png"
"$CHROME" \
--headless \
--screenshot="$OUT" \
--window-size=1280,900 \
--timeout=5000 \
"$URL"
printf 'Saved %s\n' "$OUT"
Save this as a shell script, make it executable with chmod +x capture.sh, then run ./capture.sh. In scheduled jobs or CI, use an absolute output path and ensure the Chrome binary is installed in the environment where the job runs.
6. macOS and Chrome version notes
Headless mode runs Chrome without the ordinary visible browser interface. Chrome’s current command-line reference is the source of truth for the screenshot, viewport, and timeout flags above.
There is a version caveat for older tutorials: the Chromium project says the old Headless implementation was removed from the Chrome binary starting with milestone 132. Users who specifically need that old implementation are directed to the separate Chrome Headless Shell binary. Check the installed Chrome version and consult the Chromium Headless documentation if older instructions behave differently.
7. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
No such file or directory for Chrome |
The app is installed elsewhere, or the path differs for the Chrome channel. | Confirm the Chrome app location and use that bundle’s executable path. The standard path shown here is an example, not a universal install location. |
| No screenshot appears in the expected folder | Chrome writes the default file to the process’s current working directory. | Run pwd before the command, change to the desired directory, or set --screenshot="/absolute/path/page.png". |
| The image is smaller or larger than expected | The viewport dimensions are not the expected dimensions, or the page is being mistaken for a full-page capture. | Set --window-size=WIDTH,HEIGHT explicitly. Remember that this sets the viewport and does not promise the full document height. |
| Images or page sections are missing | The page may still be loading at capture time, or content may require scrolling, interaction, or asynchronous work. | Try a longer --timeout, but treat it as a maximum wait rather than proof of readiness. If content is lazy-loaded, use a workflow that can scroll or explicitly wait for the relevant content. |
| An old tutorial’s Headless mode behaves differently | It may rely on the old Headless implementation removed from the Chrome binary beginning at M132. | Check the installed version and follow the current Chrome or Headless Shell documentation. |
| The page renders differently than in an interactive session | Sites can vary output based on viewport, authentication, cookies, scripts, timing, or bot checks. | Set a deliberate viewport and use a capture method that supports the site’s needed session and wait behavior. The basic CLI example does not promise identical output for every URL. |
8. Performance, reliability, and cost
This local command has no per-screenshot API charge, but it depends on your Mac, installed Chrome version, network, and the target page. Each capture starts browser work, so if you need repeated jobs, account for startup time and resource use in your own automation. A short timeout can finish sooner while missing late content; a long timeout can make slow or stalled pages hold up a batch.
For more reliable batches, record the URL, output path, exit status, and any Chrome error output for each job. Retry only failures that are plausibly transient, and cap retries so a page that never loads cannot stall the whole run. The command-line timeout is useful for bounding the wait, but it does not guarantee successful navigation or complete rendering.
9. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns an image or PDF; its documentation lists the available parameters.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com/ \
-o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com/"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com/'
});
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(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots, and 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000. Sign up for free and get 1,000 screenshots a month with no card.
10. FAQ
Can I save the screenshot somewhere other than the current directory?
Yes. Pass a filename or absolute path to --screenshot, such as --screenshot="/Users/me/Desktop/page.png".
Does Headless Chrome need a display server on macOS?
The --headless flag runs Chrome without its ordinary visible browser interface, which is the point of this terminal workflow.
Can I use the result as a reliable archive of a page?
It is a rendered image at a point in time, not a complete archive of the page’s source, assets, or interactive state. Dynamic content and access requirements can affect what appears.
Sources
- Chrome Headless command-line reference — screenshot output, viewport sizing, and timeout behavior.
- Chromium Headless documentation — current Headless implementation and Headless Shell version context.


