DEV Community

Ozor
Ozor

Posted on

Take Website Screenshots with Code: One API Call, No Puppeteer Setup

Taking screenshots of websites programmatically is one of those tasks that sounds simple until you actually try it. You need Puppeteer or Playwright, a headless Chromium binary, proper error handling for timeouts and navigation failures, and a server with enough memory to run a browser.

Or... you can make one API call.

The Hard Way (Puppeteer)

// First: npm install puppeteer (downloads 300MB+ Chromium)
const puppeteer = require('puppeteer');

async function screenshot(url) {
  const browser = await puppeteer.launch({
    headless: 'new',
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  });
  const page = await browser.newPage();
  await page.setViewport({ width: 1280, height: 720 });
  await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
  const buffer = await page.screenshot({ type: 'png' });
  await browser.close();
  return buffer;
}
Enter fullscreen mode Exit fullscreen mode

This works locally, but deploying it is painful. Chromium needs 400MB+ of disk, ~500MB RAM per instance, and crashes unpredictably on cheap VPS/serverless platforms.

The Easy Way (One API Call)

curl "https://agent-gateway-kappa.vercel.app/v1/agent-screenshot/api/screenshot?url=https://github.com" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -o screenshot.png
Enter fullscreen mode Exit fullscreen mode

That's it. You get back a PNG image. No browser installation, no memory management, no timeout debugging.

Getting Started

First, grab a free API key (200 free credits, no credit card):

curl -X POST https://agent-gateway-kappa.vercel.app/api/keys/create
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "key": "gw_abc123...",
  "credits": 200,
  "expiresAt": "2026-04-03T..."
}
Enter fullscreen mode Exit fullscreen mode

Each screenshot costs 1 credit. That's 200 screenshots to try it out.

JavaScript Example

const fs = require('fs');

async function takeScreenshot(url, options = {}) {
  const params = new URLSearchParams({
    url,
    viewport: options.viewport || 'desktop',
    format: options.format || 'png',
    ...(options.fullPage && { fullPage: 'true' }),
    ...(options.darkMode && { darkMode: 'true' })
  });

  const response = await fetch(
    `https://agent-gateway-kappa.vercel.app/v1/agent-screenshot/api/screenshot?${params}`,
    { headers: { 'X-Api-Key': process.env.API_KEY } }
  );

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.error || 'Screenshot failed');
  }

  return Buffer.from(await response.arrayBuffer());
}

// Usage
const screenshot = await takeScreenshot('https://github.com');
fs.writeFileSync('github.png', screenshot);
console.log('Saved screenshot (${screenshot.length} bytes)');
Enter fullscreen mode Exit fullscreen mode

Python Example

import requests

API_KEY = "your_api_key_here"
BASE_URL = "https://agent-gateway-kappa.vercel.app"

def take_screenshot(url, viewport="desktop", format="png", full_page=False):
    response = requests.get(
        f"{BASE_URL}/v1/agent-screenshot/api/screenshot",
        params={
            "url": url,
            "viewport": viewport,
            "format": format,
            "fullPage": str(full_page).lower()
        },
        headers={"X-Api-Key": API_KEY}
    )
    response.raise_for_status()
    return response.content

# Take a desktop screenshot
image = take_screenshot("https://news.ycombinator.com")
with open("hn.png", "wb") as f:
    f.write(image)
print(f"Saved {len(image)} bytes")
Enter fullscreen mode Exit fullscreen mode

Advanced: POST for More Options

The GET endpoint covers most cases, but POST gives you full control:

curl -X POST https://agent-gateway-kappa.vercel.app/v1/agent-screenshot/api/screenshot \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "viewport": "mobile",
    "fullPage": true,
    "format": "png",
    "darkMode": true,
    "delay": 2000,
    "hideSelectors": [".cookie-banner", "#popup"],
    "timeout": 15000
  }' \
  -o screenshot.png
Enter fullscreen mode Exit fullscreen mode

Available Options

Parameter Type Default Description
url string required URL to capture
viewport string desktop desktop (1280x720), mobile (375x812), tablet (768x1024)
fullPage boolean false Capture the entire scrollable page
format string png png or jpeg
quality number 80 JPEG quality (1-100)
darkMode boolean false Emulate dark mode
delay number 0 Wait N ms after page load before capturing (max 5000)
hideSelectors array [] CSS selectors to hide (cookie banners, popups, etc.)
selector string null Capture only a specific element
timeout number 15000 Navigation timeout in ms (max 30000)

Real-World Use Cases

1. Social Media Preview Cards

Generate Open Graph images for your blog posts dynamically:

app.get('/og-image', async (req, res) => {
  const { title } = req.query;
  // Create a simple HTML page with the title styled
  const htmlUrl = `https://yoursite.com/og-template?title=${encodeURIComponent(title)}`;
  const image = await takeScreenshot(htmlUrl, { viewport: 'desktop' });
  res.set('Content-Type', 'image/png');
  res.set('Cache-Control', 'public, max-age=86400');
  res.send(image);
});
Enter fullscreen mode Exit fullscreen mode

2. Visual Regression Testing

Compare screenshots before and after deployments:

import hashlib

def check_visual_regression(url):
    current = take_screenshot(url)
    current_hash = hashlib.md5(current).hexdigest()

    try:
        with open("baseline.png", "rb") as f:
            baseline_hash = hashlib.md5(f.read()).hexdigest()
    except FileNotFoundError:
        # First run — save as baseline
        with open("baseline.png", "wb") as f:
            f.write(current)
        return "baseline_saved"

    if current_hash != baseline_hash:
        with open("current.png", "wb") as f:
            f.write(current)
        return "visual_change_detected"

    return "no_changes"

result = check_visual_regression("https://yoursite.com")
print(result)
Enter fullscreen mode Exit fullscreen mode

3. Monitoring Dashboard

Take periodic screenshots of your services:

const services = [
  'https://yourapp.com',
  'https://yourapp.com/dashboard',
  'https://yourapp.com/status',
];

for (const url of services) {
  const image = await takeScreenshot(url);
  const name = new URL(url).pathname.replace(/\//g, '_') || 'home';
  fs.writeFileSync(`monitoring/${name}_${Date.now()}.png`, image);
}
Enter fullscreen mode Exit fullscreen mode

Response Headers

The API returns useful metadata in response headers:

Content-Type: image/png
X-Screenshot-Id: scr_a1b2c3d4e5f6
X-Page-Title: GitHub%3A%20Let%27s%20build%20from%20here
X-Image-Size: 84521
Enter fullscreen mode Exit fullscreen mode

X-Page-Title is URL-encoded and gives you the page's <title> tag — useful for logging or display without parsing HTML.

Pricing

  • 200 free screenshots with a new API key (no credit card)
  • After that: 1 credit per screenshot
  • Top up with USDC on Base or Monero (XMR) — pricing details

The same API key works across 40+ other APIs (geolocation, DNS lookup, crypto prices, web scraping, and more) — check the full catalog.


Get your free API key:

curl -X POST https://agent-gateway-kappa.vercel.app/api/keys/create
Enter fullscreen mode Exit fullscreen mode

Full API docs: agent-gateway-kappa.vercel.app/docs

Top comments (0)