Capture a web page as an image from Python. Standard library only - urllib, no requests, no SDK.
import json
import os
import urllib.error
import urllib.parse
import urllib.request
params = urllib.parse.urlencode({
"url": "https://example.com",
"access_key": os.environ["SCREENSHOTLINE_KEY"],
"format": "png",
"viewport_width": 1280,
"block_ads": "true",
"block_cookie_banners": "true",
})
try:
with urllib.request.urlopen("https://api.screenshotline.com/take?" + params) as r:
with open("screenshot.png", "wb") as f:
f.write(r.read())
print(r.headers["x-quota-remaining"], "renders remaining")
except urllib.error.HTTPError as e:
print("error:", json.load(e)["error"]["message"])
| Parameter | Default | What it does |
|---|---|---|
| url | required | The page to capture. http/https only; private and reserved addresses are refused. |
| format | png | png, jpeg, webp or pdf. |
| full_page | false | Capture the whole scrollable page, scrolling first so lazy images load. |
| viewport_width | 1280 | Viewport width in pixels. |
| viewport_height | 800 | Viewport height in pixels. |
| selector | — | Capture only the element matching this CSS selector. |
| block_ads | false | Block ad networks and collapse the empty slot they leave behind. |
| block_cookie_banners | false | Remove consent banners, including ones injected after load. |
| color_scheme | light | Render the page as light or dark. |
| delay | 0 | Extra wait before capture, in ms. |
| cache | false | Serve a cached image when one exists. Cache hits are never billed. |
| fail_on_blank | false | Return 502 instead of an image when the capture looks blank. |
Failures return JSON with a stable code. Successful captures return the
image bytes directly, with no envelope.
{
"error": {
"code": "invalid_access_key",
"message": "Unknown or revoked API key."
}
}
Common codes: missing_access_key (401), invalid_access_key (403),
quota_exceeded (402), render_timeout (504),
blank_capture (502).
The headline use case is putting a capture straight into an <img src>. A raw
key in public HTML is a billing incident waiting to happen, so sign the URL instead: the
signature covers every parameter, and a tampered URL is refused.
node tools/sign.js "https://example.com" full_page=true
Set REQUIRE_SIGNATURE=true to refuse unsigned requests entirely.
Every response carries your position, so you never need a second request to check:
X-Quota-Limit: 2000
X-Quota-Used: 417
X-Quota-Remaining: 1583
X-Quota-Period: 2026-09