BlogScreenshots on your device
How to Take Desktop Screenshots in Python
Capture your screen, a region, or a monitor in Python with PyAutoGUI, Pillow, and MSS, including setup, scaling, troubleshooting, and automation tips.

For a simple desktop screenshot in Python, install PyAutoGUI and call pyautogui.screenshot(). It returns a Pillow image that you can save as PNG, JPEG, or another format:
import pyautogui
image = pyautogui.screenshot()
image.save('screenshot.png')
Use region=(left, top, width, height) for part of the screen. If you need monitor selection, raw pixel access, or Pillow-native options, compare Python MSS and Pillow’s ImageGrab. This guide covers installation, complete examples, coordinate systems, multi-monitor and Retina behavior, reliability, performance, and common errors.
1. Choose the right Python screenshot library
The best API depends on what your program does before and after capture.
| Library | Best fit | Image result | Region convention | Special considerations |
|---|---|---|---|---|
| PyAutoGUI | Beginner scripts and GUI automation | Pillow image | (left, top, width, height) |
Requires Pillow and platform capture support; see the PyAutoGUI screenshot documentation. |
| Pillow ImageGrab | Applications already using Pillow | Pillow image | bbox=(left, top, right, bottom) |
Supports platform-specific monitor, window, and scaling options; see Pillow ImageGrab documentation. |
| Python MSS | Selected monitors, regions, and direct pixel data | MSS screenshot object, convertible to Pillow | Monitor or dictionary region | Check the examples for your installed version at python-mss.readthedocs.io. |
None of these should be called universally fastest. Capture time depends on the operating system, display server, resolution, scaling, remote-session setup, and what you do with the pixels afterward. Benchmark the exact workload on the machine that will run it.
2. Install and verify the environment
Create an isolated environment, then install the library you plan to use:
python -m venv .venv
# Windows
.venv\\Scripts\\activate
# macOS or Linux
source .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install pyautogui
PyAutoGUI documents additional operating-system requirements, including Linux capture packages. Follow the current installation instructions for your distribution rather than copying a dependency list from an old tutorial. Pillow and MSS have their own platform behavior and dependencies.
Verify that Python can import the package before debugging a capture:
python -c "import pyautogui; print(pyautogui.size())"
If this prints your desktop dimensions, the import and basic display connection work. A successful import does not guarantee permission to capture pixels; operating-system privacy controls and headless sessions can still block the operation.
3. Capture the entire desktop with PyAutoGUI
PyAutoGUI’s screenshot function returns a Pillow image. Passing a filename is a shorter documented form, while explicitly calling save() makes the two steps clear:

import pyautogui
# Capture and save in two explicit steps
image = pyautogui.screenshot()
image.save('desktop.png')
# Equivalent shorthand
pyautogui.screenshot('desktop-shortcut.png')
PNG is a good default for screenshots containing text because it is lossless. JPEG creates smaller files for photographic content but introduces compression artifacts around sharp text. Pillow can also write WebP when your installed Pillow build supports it.
Always close or flush files when writing repeatedly. The following helper creates a timestamped filename and reports the captured dimensions:
from datetime import datetime
from pathlib import Path
import pyautogui
out_dir = Path('captures')
out_dir.mkdir(exist_ok=True)
name = datetime.now().strftime('desktop-%Y%m%d-%H%M%S.png')
path = out_dir / name
image = pyautogui.screenshot()
image.save(path, format='PNG')
print(f'Saved {path} ({image.width}x{image.height})')
4. Capture a region of the screen
PyAutoGUI uses a tuple in left, top, width, height order. This captures a 640 by 480 rectangle whose top-left corner is at (100, 100):
import pyautogui
image = pyautogui.screenshot(region=(100, 100, 640, 480))
image.save('region.png')
The coordinates are desktop coordinates, not image coordinates. On a multi-monitor desktop, the origin and whether secondary monitors use negative coordinates depend on the operating system and display arrangement. Print pyautogui.size() and test a small region before relying on hard-coded positions.
A safer pattern is to define the rectangle as named values and validate its dimensions:
import pyautogui
left, top, width, height = 100, 100, 640, 480
if width <= 0 or height <= 0:
raise ValueError('width and height must be positive')
screen_width, screen_height = pyautogui.size()
print(f'Desktop reports {screen_width}x{screen_height}')
image = pyautogui.screenshot(region=(left, top, width, height))
image.save('validated-region.png')
5. Use Pillow ImageGrab when you need Pillow options
Pillow’s ImageGrab.grab() captures the screen directly into a Pillow image. Its bbox uses bounding-box endpoints: (left, top, right, bottom), so the equivalent of the PyAutoGUI example is:
from PIL import ImageGrab
image = ImageGrab.grab(bbox=(100, 100, 740, 580))
image.save('pillow-region.png')
Do not mix the two conventions. Passing width and height as the third and fourth values will produce the wrong rectangle. Pillow’s documentation also describes Windows multi-screen behavior, macOS window capture, and platform-specific options. Read those details when you need a particular monitor or application window.
On macOS Retina displays, Pillow documents captures at 2x size by default and provides scale_down=True for 1x output. Check the returned dimensions instead of assuming that desktop points equal image pixels:
from PIL import ImageGrab
image = ImageGrab.grab()
print(image.mode, image.size)
image.save('retina-aware.png')
Pillow documents platform-specific modes as well: macOS captures can be RGBA, while other platforms commonly return RGB. If downstream code requires a predictable mode, convert it explicitly:
from PIL import ImageGrab
image = ImageGrab.grab().convert('RGB')
image.save('rgb-desktop.jpg', quality=92)
6. Capture a monitor or raw pixels with MSS
MSS is useful when your code needs to select a monitor or process pixel data directly. The monitor list normally includes a virtual combined desktop followed by individual monitors. Select the entry appropriate for your setup and inspect the list first:
from mss import MSS
with MSS() as sct:
for index, monitor in enumerate(sct.monitors):
print(index, monitor)
# A common layout uses monitor 1 for the first physical monitor.
shot = sct.grab(sct.monitors[1])
image = shot.to_pil()
image.save('monitor.png')
For a custom rectangle, pass a dictionary with left, top, width, and height:
from mss import MSS
area = {'left': 100, 'top': 100, 'width': 640, 'height': 480}
with MSS() as sct:
shot = sct.grab(area)
shot.to_pil().save('mss-region.png')
MSS also exposes raw BGRA data for computer-vision pipelines. Convert to Pillow only when you need image methods or file output. Consult the current MSS examples for the exact object attributes in your version.
7. Window capture, multi-monitor layouts, and scaling
Desktop capture libraries do not all provide the same window-selection API. Pillow documents platform-specific window options; PyAutoGUI and MSS generally work from screen coordinates or monitor rectangles. If you need a window, discover its bounds with a window-management library appropriate to your OS, then pass those bounds to the capture library.

Use this checklist for coordinate bugs:
- Print the reported monitor and desktop sizes.
- Confirm whether the operating system uses logical points or physical pixels.
- Check display scaling settings and Retina behavior.
- Test one monitor at a time before using the virtual desktop rectangle.
- Account for negative coordinates when a monitor is positioned left or above the primary display.
When a screenshot looks offset, log both the requested rectangle and the returned image size. A mismatch usually indicates scaling or an incorrect region convention rather than a corrupted image.
8. Post-process, resize, and save safely
Pillow lets you crop, resize, annotate, and convert after capture. Use a high-quality resampling filter when reducing a large screenshot:
import pyautogui
from PIL import Image
image = pyautogui.screenshot()
max_width = 1600
if image.width > max_width:
ratio = max_width / image.width
image = image.resize((max_width, round(image.height * ratio)), Image.Resampling.LANCZOS)
image.save('resized.png', optimize=True)
For sensitive desktops, decide where files are written and how long they are retained. Screenshots can contain passwords, tokens, private messages, or customer data. Restrict output permissions, avoid logging image contents, and delete temporary captures when a job finishes.
9. Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
ModuleNotFoundError |
Package installed into a different Python environment | Activate the intended virtual environment and run python -m pip install ... with that interpreter. |
| Linux capture fails or returns a blank image | Display backend or external capture dependency is unavailable | Follow the selected library’s current Linux instructions. Pillow documents fallbacks such as gnome-screenshot, grim, and spectacle for some setups; these do not apply automatically to every library. |
| macOS permission error or black result | Screen-recording privacy permission or a restricted session | Review the current macOS privacy settings for the Python executable or terminal you use, then restart the process. |
| Wrong crop position | Confused width/height with right/bottom | Use PyAutoGUI’s (left, top, width, height) or Pillow’s (left, top, right, bottom) exactly. |
| Only one monitor appears | Library or display backend exposes a single monitor | Inspect sct.monitors with MSS or the multi-screen options in Pillow; verify the desktop session is not isolated. |
| Image dimensions are twice expected on Retina | Physical-pixel capture at 2x scale | Use Pillow’s documented scale_down=True where appropriate, or map coordinates using the returned dimensions. |
| Works locally but fails in CI or SSH | No interactive display session | Desktop screenshot APIs need an available display. Configure a supported virtual display for your test environment or capture the webpage remotely instead. |
10. Performance and reliability guidance
Capture cost grows with resolution and with every conversion or resize. For repeated captures:
- Capture only the region you need.
- Reuse an MSS context instead of repeatedly creating one when your workload allows it.
- Convert to Pillow only when required.
- Write files asynchronously if disk I/O blocks your main loop.
- Measure capture, conversion, encoding, and upload as separate timings.
Do not use an old documentation estimate as a current benchmark. Test on the target OS, display server, resolution, scaling mode, and workload. Add retries only for transient environment failures; retrying a permission error will not fix it. For automation, verify that the image has the expected dimensions and nonempty pixel data before sending it downstream.
11. Or skip the browser setup: ScreenshotNeo
PyAutoGUI, Pillow, and MSS capture the desktop attached to the Python process. If your goal is a screenshot of a public webpage, a hosted browser screenshot API avoids display drivers, GUI sessions, and coordinate scaling. ScreenshotNeo accepts one GET request and returns PNG, JPEG, WebP, or PDF. Its clean-shot flow accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be disabled.
Only clean shots are billed. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing result. ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
Read the complete option list in the ScreenshotNeo API documentation. Features include full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, PDF paper and margin controls, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed image links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs per call, usage reporting, and an OpenAPI specification.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
Python
import requests
r = requests.get(
'https://api.screenshotneo.com/v1/shot',
params={'access_key': 'YOUR_API_KEY', 'url': 'https://stripe.com'},
timeout=90,
)
r.raise_for_status()
open('shot.webp', 'wb').write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
The Free plan includes 1,000 screenshots per month without a card. Paid plans start at $5 for 3,000 shots; every feature is available on every plan. Create a free ScreenshotNeo account and get 1,000 screenshots a month with no card.
12. FAQ
Can Python take a screenshot without opening a browser?
Yes. PyAutoGUI, Pillow ImageGrab, and MSS capture the current desktop directly. They still require an accessible display session.
Which format should I use?
Use PNG for crisp text and lossless editing. Use JPEG for photographic images where a smaller file matters. Use WebP when your consumers support it and you want a modern compressed format.
Why is my region the wrong size?
Check the API’s coordinate convention and display scaling. PyAutoGUI takes width and height; Pillow takes right and bottom endpoints.
Can I capture a webpage on a server?
Desktop libraries need a display. A hosted browser API such as ScreenshotNeo is designed for URL capture without a local GUI session.


