Back to blog
· May 27, 2026· 2 min read
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
Creator & Maker
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
isFullPagecan take several seconds. - Use
responseCachewhen re-running the same script during development so you are not billed for every iteration.