Convert Website to Image with an API: The Complete Guide

Published March 2, 2026 · 10 min read

Need to turn a URL into a PNG or JPEG? Maybe you're generating thumbnails for a link directory, creating previews for a CMS, or building a portfolio of website designs. Whatever the use case, converting a website to an image programmatically used to mean spinning up a headless browser, managing dependencies, and debugging rendering issues.

Not anymore. A website-to-image API handles the hard parts: browser rendering, JavaScript execution, font loading, cookie banners, and output optimization. You send a URL, you get an image back.

This guide covers how to convert any website to an image using GrabShot's API, with working code in curl, Node.js, and Python.

Why Use an API Instead of a Headless Browser?

Running Puppeteer or Playwright yourself works, but comes with overhead:

An API abstracts all of this. One HTTP request, one image back. The API provider handles the browsers, the rendering quirks, and the scaling.

Quick Start: Your First Website-to-Image Conversion

The simplest possible call. No authentication needed for the free tier:

curl

curl "https://grabshot.dev/api/screenshot?url=https://example.com&format=png" \
  -o example.png

That's it. You now have a PNG of example.com saved locally. Let's look at what you can customize.

Node.js

const fs = require('fs');

async function websiteToImage(url, outputPath) {
  const params = new URLSearchParams({
    url,
    format: 'png',
    width: '1280',
    height: '800'
  });

  const response = await fetch(
    `https://grabshot.dev/api/screenshot?${params}`,
    {
      headers: { 'x-api-key': process.env.GRABSHOT_API_KEY }
    }
  );

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  const buffer = Buffer.from(await response.arrayBuffer());
  fs.writeFileSync(outputPath, buffer);
  console.log(`Saved ${outputPath} (${buffer.length} bytes)`);
}

websiteToImage('https://github.com', 'github.png');

Python

import requests
import os

def website_to_image(url: str, output_path: str, **options):
    params = {
        "url": url,
        "format": options.get("format", "png"),
        "width": options.get("width", 1280),
        "height": options.get("height", 800),
    }
    headers = {"x-api-key": os.environ.get("GRABSHOT_API_KEY", "")}

    resp = requests.get(
        "https://grabshot.dev/api/screenshot",
        params=params,
        headers=headers,
    )
    resp.raise_for_status()

    with open(output_path, "wb") as f:
        f.write(resp.content)

    print(f"Saved {output_path} ({len(resp.content):,} bytes)")

website_to_image("https://github.com", "github.png")

Output Formats: PNG vs JPEG vs WebP

The format parameter controls the output type. Each has trade-offs:

FormatBest ForFile SizeQuality
PNGUI screenshots, text-heavy pages, transparencyLargerLossless
JPEGPhoto-heavy pages, thumbnails, previewsSmallerAdjustable (1-100)
WebPWeb display, modern browsersSmallestAdjustable

For thumbnails and previews where file size matters, JPEG at quality 80 is a good default. For pixel-perfect captures where you need crisp text, use PNG.

# JPEG at 85% quality - good balance of size and clarity
curl "https://grabshot.dev/api/screenshot?url=https://example.com&format=jpeg&quality=85" \
  -o example.jpg

# WebP for smallest file size
curl "https://grabshot.dev/api/screenshot?url=https://example.com&format=webp&quality=80" \
  -o example.webp

Full-Page Captures

By default, the API captures the visible viewport. To capture the entire scrollable page, set fullPage=true:

curl "https://grabshot.dev/api/screenshot?url=https://example.com&fullPage=true" \
  -o full-page.png

Full-page captures are useful for archiving, documentation, and design reviews. Be aware that very long pages (10,000px+) will produce large files. Combine with JPEG format and a reasonable quality setting to keep file sizes manageable.

Custom Viewports and Retina Output

Control the browser viewport size and pixel density:

# Mobile viewport
curl "https://grabshot.dev/api/screenshot?url=https://example.com&width=390&height=844" \
  -o mobile.png

# Tablet viewport
curl "https://grabshot.dev/api/screenshot?url=https://example.com&width=768&height=1024" \
  -o tablet.png

# Retina (2x pixel density)
curl "https://grabshot.dev/api/screenshot?url=https://example.com&width=1280&height=800&deviceScaleFactor=2" \
  -o retina.png

The deviceScaleFactor=2 parameter renders at 2x resolution, giving you a 2560x1600 image from a 1280x800 viewport. This is essential for retina displays and high-DPI marketing materials.

Waiting for Dynamic Content

Modern websites load content asynchronously. SPAs, lazy-loaded images, and animations need time to render. The API provides several strategies:

# Wait for a specific element to appear
curl "https://grabshot.dev/api/screenshot?url=https://example.com&waitForSelector=.main-content" \
  -o waited.png

# Wait a fixed delay (milliseconds)
curl "https://grabshot.dev/api/screenshot?url=https://example.com&delay=3000" \
  -o delayed.png

The waitForSelector approach is more reliable than a fixed delay because it adapts to actual page load speed. Use CSS selectors that target the content you care about.

Real-World Use Cases

1. Link Preview Thumbnails

Build a bookmark manager or link aggregator that shows visual previews of saved URLs. Capture at a small viewport, compress as JPEG:

async function generateThumbnail(url) {
  const params = new URLSearchParams({
    url,
    width: '640',
    height: '400',
    format: 'jpeg',
    quality: '75'
  });

  const res = await fetch(
    `https://grabshot.dev/api/screenshot?${params}`,
    { headers: { 'x-api-key': process.env.GRABSHOT_API_KEY } }
  );

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

2. Automated Report Generation

Capture dashboards or analytics pages on a schedule and email them to stakeholders:

import requests
import smtplib
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart

def capture_dashboard():
    resp = requests.get("https://grabshot.dev/api/screenshot", params={
        "url": "https://your-app.com/dashboard",
        "width": 1440,
        "height": 900,
        "format": "png",
        "delay": 5000,  # wait for charts to render
    }, headers={
        "x-api-key": "your-api-key"
    })
    return resp.content

# Capture and attach to email
img_data = capture_dashboard()
msg = MIMEMultipart()
msg.attach(MIMEImage(img_data, name="dashboard.png"))

3. Website Directory / Portfolio

If you run a directory of websites, tools, or templates, you need screenshots of every listed site. Batch processing makes this straightforward:

const urls = [
  'https://stripe.com',
  'https://linear.app',
  'https://vercel.com',
  'https://notion.so'
];

async function batchCapture(urls) {
  const results = await Promise.allSettled(
    urls.map(async (url) => {
      const slug = new URL(url).hostname.replace(/\./g, '-');
      const params = new URLSearchParams({
        url,
        width: '1280',
        height: '800',
        format: 'webp',
        quality: '80'
      });

      const res = await fetch(
        `https://grabshot.dev/api/screenshot?${params}`,
        { headers: { 'x-api-key': process.env.GRABSHOT_API_KEY } }
      );

      if (!res.ok) throw new Error(`${url}: ${res.status}`);
      const buf = Buffer.from(await res.arrayBuffer());
      require('fs').writeFileSync(`screenshots/${slug}.webp`, buf);
      return { url, size: buf.length };
    })
  );

  results.forEach((r, i) => {
    if (r.status === 'fulfilled') {
      console.log(`OK: ${r.value.url} (${r.value.size} bytes)`);
    } else {
      console.error(`FAIL: ${urls[i]} - ${r.reason.message}`);
    }
  });
}

batchCapture(urls);

Start Converting Websites to Images

25 free screenshots per month. No credit card required. API key in 30 seconds.

Try It Free →

Handling Edge Cases

Some pages are trickier than others. Here are common issues and how to handle them:

API Parameters Reference

ParameterDefaultDescription
urlrequiredThe URL to capture
formatpngOutput format: png, jpeg, webp
width1280Viewport width in pixels
height800Viewport height in pixels
quality80JPEG/WebP quality (1-100)
fullPagefalseCapture entire scrollable page
deviceScaleFactor1Pixel density (1, 2, or 3)
delay0Wait time in ms before capture
waitForSelector-CSS selector to wait for
colorSchemelightlight or dark

For the full API reference with all parameters, check the documentation.

Pricing

GrabShot offers a free tier with 25 screenshots per month, which is enough to test and prototype. Paid plans start at $9/month for 2,000 captures, with higher tiers for production workloads. See the pricing page for details.

Wrapping Up

Converting a website to an image is a solved problem. You don't need to manage Chromium instances, fight rendering bugs, or scale browser pools. A single API call gives you a high-quality image of any URL, in the format and resolution you need.

The code examples above are copy-paste ready. Grab a free API key and start capturing.