Platform

Examples

Submit-and-poll in the two most common stacks. Both run server-side — never expose your key to browsers. Neither runs today: they are written for the day direct API access opens, and a key from your dashboard will not authenticate either of them yet.

not callable yet

Direct API access is not open during the beta. API keys created in the dashboard authenticate nothing — no request anywhere is accepted with one — and screenshots are captured from the dashboard instead.

These pages describe the capture service the dashboard talks to. Read every request below as the shape of the interface that will open, not as a call you can make today.

To capture something now, use the dashboard. It submits the same job, polls it every five seconds, and hands back the same hosted image URL.

Node / Next.js server action

screenshot.ts
const BASE = "https://api.previewapi.dev/api/v1";
const KEY = process.env.PREVIEWAPI_KEY;

export async function capture(url) {
  const job = await fetch(`${BASE}/screenshots`, {
    method: "POST",
    headers: { "Content-Type": "application/json",
               Authorization: `Bearer ${KEY}` },
    body: JSON.stringify({ url, mode: "viewport", format: "png" })
  }).then(r => r.json());

  const deadline = Date.now() + 120_000;

  while (Date.now() < deadline) {
    await new Promise(r => setTimeout(r, 5000));
    const s = await fetch(`${BASE}/screenshots/${job.job_uuid}`, {
      headers: { Authorization: `Bearer ${KEY}` }
    }).then(r => r.json());
    if (s.status === "completed") return s.image_url;
    if (s.status === "failed") throw new Error(s.error);
  }

  throw new Error("Capture did not finish within 2 minutes");
}

Python

screenshot.py
import os, time, requests

BASE = "https://api.previewapi.dev/api/v1"
H = {"Authorization": f"Bearer {os.environ['PREVIEWAPI_KEY']}"}

def capture(url):
    job = requests.post(f"{BASE}/screenshots", headers=H,
        json={"url": url, "mode": "viewport", "format": "png"}).json()

    deadline = time.monotonic() + 120
    while time.monotonic() < deadline:
        time.sleep(5)
        s = requests.get(f"{BASE}/screenshots/{job['job_uuid']}", headers=H).json()
        if s["status"] == "completed":
            return s["image_url"]
        if s["status"] == "failed":
            raise Exception(s["error"])

    raise Exception("Capture did not finish within 2 minutes")

Both loops stop after two minutes rather than polling forever, which is also what the dashboard does — a job that has said nothing for that long is settled by the service, not by the client.