DEV Community

XSron Hou
XSron Hou

Posted on Originally published at scrapio.dev

Take Screenshots of Any Website via API

Originally posted on the Scrapio blog — sharing here too.

Screenshots via API have more use cases than you might expect: visual regression testing, monitoring landing pages for unauthorized changes, generating OG image previews, archiving pages, and building internal dashboards with live page previews.

What you'll need

  • A Scrapio API key
  • curl or Python

Take a basic screenshot

curl -X POST https://api.scrapio.dev/v1/fetch \
  -H "Authorization: Bearer sk-..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "output": ["screenshot"],
    "render_js": true
  }'
Enter fullscreen mode Exit fullscreen mode

Screenshot output always requires render_js: true — without it, the request is routed to a non-browser executor that can't capture a screenshot and the call fails.

The response contains a screenshot object with a url field pointing to the captured image:

{
  "request_id": "req_abc123",
  "mode": "inline",
  "status": "completed",
  "outputs": {
    "screenshot": {
      "url": "https://storage.scrapio.dev/screenshots/req_abc123.png"
    }
  },
  "usage": { "credits": 2 }
}
Enter fullscreen mode Exit fullscreen mode

Capture a mobile viewport

Pass device: "mobile" to render the page at a mobile viewport:

import httpx

resp = httpx.post(
    "https://api.scrapio.dev/v1/fetch",
    headers={"Authorization": "Bearer sk-..."},
    json={
        "url": "https://example.com",
        "output": ["screenshot"],
        "render_js": True,
        "device": "mobile",
    },
    timeout=30,
)
screenshot_url = resp.json()["outputs"]["screenshot"]["url"]
print(screenshot_url)
Enter fullscreen mode Exit fullscreen mode

device accepts "desktop" (default), "mobile", or "tablet".

Wait for JavaScript content to finish loading

render_js: true is required for every screenshot request. For pages that load content asynchronously, pair it with wait_for:

resp = httpx.post(
    "https://api.scrapio.dev/v1/fetch",
    headers={"Authorization": "Bearer sk-..."},
    json={
        "url": "https://example.com/dashboard",
        "output": ["screenshot"],
        "render_js": True,
        "wait_for": {"network_idle": True},
    },
    timeout=30,
)
screenshot_url = resp.json()["outputs"]["screenshot"]["url"]
Enter fullscreen mode Exit fullscreen mode

wait_for: {network_idle: true} tells Scrapio to hold until all network requests settle before capturing — useful for charts and dashboards that load data asynchronously.

Combine screenshot with markdown

You can request multiple outputs in a single call:

resp = httpx.post(
    "https://api.scrapio.dev/v1/fetch",
    headers={"Authorization": "Bearer sk-..."},
    json={
        "url": "https://example.com/article",
        "output": ["screenshot", "markdown"],
        "render_js": True,
    },
    timeout=30,
)
outputs = resp.json()["outputs"]
screenshot_url = outputs["screenshot"]["url"]
text = outputs["markdown"]
Enter fullscreen mode Exit fullscreen mode

Visual monitoring workflow

Download the screenshot and diff against a baseline using Pillow:

import httpx
from PIL import Image, ImageChops
import io

def screenshot_url(url: str) -> Image.Image:
    resp = httpx.post(
        "https://api.scrapio.dev/v1/fetch",
        headers={"Authorization": "Bearer sk-..."},
        json={"url": url, "output": ["screenshot"], "render_js": True},
        timeout=30,
    )
    img_url = resp.json()["outputs"]["screenshot"]["url"]
    img_bytes = httpx.get(img_url).content
    return Image.open(io.BytesIO(img_bytes))

def has_visual_change(baseline: Image.Image, current: Image.Image) -> bool:
    diff = ImageChops.difference(baseline, current)
    return diff.getbbox() is not None
Enter fullscreen mode Exit fullscreen mode

Next steps

Top comments (0)