DEV Community

miccho27
miccho27

Posted on

Capture Website Screenshots at Scale: Screenshot API for Developers

Capture Website Screenshots Without Running Headless Browsers

Generating screenshots programmatically is a solved problem—but the implementation details are painful. You need Puppeteer, Selenium, or Playwright. You need infrastructure to run them. You need to manage timeouts, retries, and memory leaks.

Or you use an API and ship the problem to someone else.

The Problem: Screenshots Are Hard at Scale

Running headless browsers is resource-intensive:

  • Memory per instance: 100–300MB
  • Time per screenshot: 2–5 seconds
  • Infrastructure cost: High. Very high.
  • Maintenance burden: Browser crashes, dependency hell, memory leaks

The Screenshot API solves this by offloading rendering to edge-optimized infrastructure. You just send a URL; you get back a PNG in under 2 seconds.

Real-World Use Case: Link Preview Thumbnails

You're building a social media scheduler or content curation tool. Users want to preview how their links will look when shared.

Without screenshots:

  • User pastes a URL
  • You fetch meta tags (og:image, og:title)
  • Hope it looks good
  • Surprise: the preview image is broken or missing

With screenshots:

  • User pastes a URL
  • You generate a thumbnail in real-time
  • Display exactly what will appear on social media
  • User adjusts if needed before scheduling

Python Implementation

import requests
import base64
from pathlib import Path

RAPIDAPI_KEY = "YOUR_RAPIDAPI_KEY"
RAPIDAPI_HOST = "screenshot-api.p.rapidapi.com"

def capture_website_screenshot(url: str, width: int = 1280, height: int = 720, full_page: bool = False) -> bytes:
    """
    Capture a website screenshot and return PNG bytes.

    Args:
        url: Website URL to screenshot
        width: Viewport width (default 1280px)
        height: Viewport height (default 720px)
        full_page: Capture full scrollable page (default False)

    Returns:
        PNG image bytes
    """
    api_url = "https://screenshot-api.p.rapidapi.com/capture"

    params = {
        "url": url,
        "width": width,
        "height": height,
        "full_page": "true" if full_page else "false"
    }

    headers = {
        "X-RapidAPI-Key": RAPIDAPI_KEY,
        "X-RapidAPI-Host": RAPIDAPI_HOST
    }

    response = requests.get(api_url, headers=headers, params=params)
    response.raise_for_status()

    # Response is PNG binary data
    return response.content

# Example usage
url = "https://github.com/torvalds/linux"
image_bytes = capture_website_screenshot(url, width=1280, height=720)

# Save to file
Path("screenshot.png").write_bytes(image_bytes)
print(f"Screenshot saved ({len(image_bytes)} bytes)")
Enter fullscreen mode Exit fullscreen mode

Express.js: API Endpoint

const axios = require("axios");
const express = require("express");
const app = express();

const captureScreenshot = async (url, options = {}) => {
  const response = await axios.get(
    "https://screenshot-api.p.rapidapi.com/capture",
    {
      params: {
        url,
        width: options.width || 1280,
        height: options.height || 720,
        full_page: options.fullPage ? "true" : "false"
      },
      headers: {
        "X-RapidAPI-Key": process.env.RAPIDAPI_KEY,
        "X-RapidAPI-Host": "screenshot-api.p.rapidapi.com"
      },
      responseType: "arraybuffer"
    }
  );

  return Buffer.from(response.data, "binary");
};

// Endpoint: Generate preview for link
app.post("/api/preview", async (req, res) => {
  const { url } = req.body;

  if (!url) {
    return res.status(400).json({ error: "URL required" });
  }

  try {
    const screenshot = await captureScreenshot(url, {
      width: 600,
      height: 400
    });

    res.set("Content-Type", "image/png");
    res.send(screenshot);

  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Endpoint: Save preview to storage
app.post("/api/posts", async (req, res) => {
  const { title, url, content } = req.body;

  try {
    // Generate screenshot
    const screenshot = await captureScreenshot(url, { width: 1200, height: 630 });

    // Upload to cloud storage (S3, GCS, etc.)
    const imageUrl = await uploadToStorage(screenshot, `preview-${Date.now()}.png`);

    // Save post with preview image
    const post = await createPost({
      title,
      url,
      content,
      preview_image: imageUrl
    });

    res.json({ success: true, post });

  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(3000, () => console.log("Preview API running"));
Enter fullscreen mode Exit fullscreen mode

Advanced Features

Full-Page Screenshots for Archival

Capture entire scrollable pages for archival or documentation:

# Capture full scrollable page (not just viewport)
def archive_webpage(url: str) -> bytes:
    """Capture full page for web archival."""
    params = {
        "url": url,
        "full_page": "true",  # Include everything below the fold
        "width": 1920,
        "height": 1080
    }

    response = requests.get(
        "https://screenshot-api.p.rapidapi.com/capture",
        headers={...},
        params=params
    )

    return response.content

# Example: Archive legal documents before they're deleted
archive_bytes = archive_webpage("https://company.com/terms-of-service")
Path(f"legal-archive-{datetime.now().isoformat()}.png").write_bytes(archive_bytes)
Enter fullscreen mode Exit fullscreen mode

Batch Processing with Concurrency

Process multiple URLs in parallel with rate limiting:

import asyncio
import aiohttp

async def batch_screenshots(urls: list, max_concurrent: int = 5) -> dict:
    """Capture multiple screenshots concurrently."""

    semaphore = asyncio.Semaphore(max_concurrent)

    async def fetch_screenshot(session, url):
        async with semaphore:
            try:
                async with session.get(
                    "https://screenshot-api.p.rapidapi.com/capture",
                    params={"url": url},
                    headers={...}
                ) as resp:
                    return {
                        "url": url,
                        "success": resp.status == 200,
                        "data": await resp.read()
                    }
            except Exception as e:
                return {
                    "url": url,
                    "success": False,
                    "error": str(e)
                }

    async with aiohttp.ClientSession() as session:
        tasks = [fetch_screenshot(session, url) for url in urls]
        results = await asyncio.gather(*tasks)

    return {r["url"]: r for r in results}

# Usage
urls = [
    "https://github.com/torvalds/linux",
    "https://stackoverflow.com",
    "https://dev.to"
]

results = asyncio.run(batch_screenshots(urls, max_concurrent=3))
for url, result in results.items():
    if result["success"]:
        Path(f"screenshot-{hash(url)}.png").write_bytes(result["data"])
Enter fullscreen mode Exit fullscreen mode

API Parameters

Parameter Type Default Notes
url string Website URL to capture
width integer 1280 Viewport width (px)
height integer 720 Viewport height (px)
full_page boolean false Capture full scrollable page
delay integer 0 Wait (ms) before capturing (for JS render)
format string png Output format (png, jpeg)

Performance Tips

1. Match Mobile Viewports for Social Sharing

Different platforms need different dimensions:

SOCIAL_VIEWPORTS = {
    "twitter": {"width": 1024, "height": 512},
    "facebook": {"width": 1200, "height": 630},
    "linkedin": {"width": 1200, "height": 627},
    "instagram": {"width": 1080, "height": 1080},
}

for platform, dims in SOCIAL_VIEWPORTS.items():
    screenshot = capture_website_screenshot(
        url,
        width=dims["width"],
        height=dims["height"]
    )
    Path(f"preview-{platform}.png").write_bytes(screenshot)
Enter fullscreen mode Exit fullscreen mode

2. Add Delay for JavaScript-Heavy Sites

Some sites need time to render JavaScript:

params = {
    "url": url,
    "delay": 2000,  # Wait 2 seconds for JS
    "width": 1280,
    "height": 720
}
Enter fullscreen mode Exit fullscreen mode

3. Cache Screenshots

Don't re-capture the same URL twice:

import hashlib

cache = {}

def cached_screenshot(url: str) -> bytes:
    """Cache screenshots to avoid re-fetching."""
    url_hash = hashlib.md5(url.encode()).hexdigest()

    if url_hash in cache:
        return cache[url_hash]

    screenshot = capture_website_screenshot(url)
    cache[url_hash] = screenshot

    return screenshot
Enter fullscreen mode Exit fullscreen mode

Use Cases

Perfect for:

  • Link preview thumbnails for social schedulers
  • Web archival and documentation
  • Legal compliance snapshots
  • QA testing visual regression
  • SEO preview generation
  • Screenshot-based dashboards

Not ideal for:

  • High-frequency polling (>100 captures/min)
  • Real-time page monitoring
  • Pixel-perfect rendering requirements

Pricing

Plan Cost Captures/mo Rate Limit
Free $0 100 1 req/sec
Pro $9.99 10,000 10 req/sec
Ultra $29.99 100,000 50 req/sec

Final Thoughts

Screenshots are a powerful way to preview, archive, and validate web content. By offloading rendering to an API, you avoid infrastructure complexity while getting fast, reliable results.

Get started free at RapidAPI Marketplace.


What would you build with screenshot automation? Share your project in the comments!

Top comments (0)