Platform
Examples
Submit-and-poll in the two most common stacks. Both run server-side — never expose your key to browsers.
Prefer not to write code? Submit a URL from the dashboard instead. 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://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://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.