Back to blog
TutorialsPythonAPIGetting started

Website screenshots from Python in 20 lines

Use requests to capture a page, handle errors properly, and save the result - plus a batch version for a list of URLs.

Thien Nguyen
Thien Nguyen
Creator & Maker
· May 27, 2026· 2 min read

Python is a natural fit for scraping, reporting and data pipelines, and screenshots often end up in all three. Here is the smallest useful client.

The basics

import os
import requests

API = "https://api.snapopa.com/capture"
HEADERS = {"Authorization": f"Bearer {os.environ['SNAPOPA_API_KEY']}"}


def capture(url: str, **options) -> dict:
    res = requests.post(API, headers=HEADERS, json={"url": url, **options}, timeout=90)
    body = res.json()
    if not res.ok or not body.get("success"):
        raise RuntimeError(body.get("error") or f"HTTP {res.status_code}")
    return body["data"]


shot = capture("https://example.com", format="png", isFullPage=True)
print(shot["fileUrl"])

Options map one-to-one to the JSON body, so anything in the API reference works as a keyword argument.

Saving the image

img = requests.get(shot["fileUrl"], timeout=30)
with open("example.png", "wb") as f:
    f.write(img.content)

Capturing a list of URLs

For a handful of pages a thread pool is plenty:

from concurrent.futures import ThreadPoolExecutor

urls = ["https://example.com", "https://example.org", "https://example.net"]

with ThreadPoolExecutor(max_workers=4) as pool:
    for url, data in zip(urls, pool.map(lambda u: capture(u, format="webp"), urls)):
        print(url, "->", data["fileUrl"])

Keep max_workers within your plan's rate limit. For hundreds or thousands of URLs, the dashboard's Bulk tool or async captures are a better fit.

Tips

  • Set a generous client timeout. Heavy pages with isFullPage can take several seconds.
  • Use responseCache when re-running the same script during development so you are not billed for every iteration.
Try Snapopa

Reading about screenshots? Try the API.

100 free tokens on signup. No credit card. One request is all it takes.

$ curl https://api.snapopa.com/capture \
    -H "Authorization: Bearer sk_•••" \
    -d '{"url":"https://stripe.com"}'

✓ 200 OK
{ "data": { "fileUrl": "https://cdn.snapopa.com/…" } }