Generate Email-Safe Website Previews with a Screenshot API

2026-05-16 | Tags: [screenshot-api, email, tutorial, marketing]

You want to show a website preview in an email. Maybe it's a weekly report showing dashboard snapshots. Maybe it's a notification email showing what a shared page looks like. Maybe it's a marketing email showcasing customer websites.

The problem: emails don't support JavaScript, iframes, or dynamic content. Every email client strips them. The only reliable way to show a website in an email is as a static image.

A screenshot API solves this cleanly.

The Basic Approach

Generate a screenshot at send time and embed it as an image:

import requests

def get_preview_image(url):
    """Get a website preview image suitable for email."""
    response = requests.get(
        "https://hermesforge.dev/api/screenshot",
        params={
            "url": url,
            "width": 600,       # Email-friendly width
            "height": 400,      # Reasonable preview height
            "format": "png",    # Universal email support
            "block_ads": "true" # Clean preview
        }
    )
    return response.content if response.status_code == 200 else None

Option 1: Inline as Base64

For transactional emails (reports, notifications), embed the image directly:

import base64
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def build_preview_email(url, recipient):
    image_data = get_preview_image(url)
    if not image_data:
        return None

    b64_image = base64.b64encode(image_data).decode()

    html = f"""
    <html>
    <body>
        <h2>Website Preview</h2>
        <p>Here's how <a href="{url}">{url}</a> looks right now:</p>
        <img src="data:image/png;base64,{b64_image}"
             alt="Preview of {url}"
             style="max-width:600px; border:1px solid #ddd; border-radius:4px;" />
        <p><a href="{url}">Visit the page →</a></p>
    </body>
    </html>
    """

    msg = MIMEMultipart("alternative")
    msg["Subject"] = f"Preview: {url}"
    msg["To"] = recipient
    msg.attach(MIMEText(html, "html"))
    return msg

Note: Base64 images work in most modern email clients but some (notably Outlook desktop) may block them. For maximum compatibility, use hosted images (Option 2).

For marketing emails or when base64 is too large, host the screenshot and reference it by URL:

import hashlib
import os

HOSTED_DIR = "/var/www/previews"
HOSTED_URL = "https://yoursite.com/previews"

def get_hosted_preview(url):
    """Generate, cache, and return a hosted URL for the preview."""
    cache_key = hashlib.md5(url.encode()).hexdigest()
    filename = f"{cache_key}.png"
    filepath = os.path.join(HOSTED_DIR, filename)

    if not os.path.exists(filepath):
        image = get_preview_image(url)
        if image:
            with open(filepath, "wb") as f:
                f.write(image)

    return f"{HOSTED_URL}/{filename}"

Then in your email template:

<img src="https://yoursite.com/previews/abc123.png"
     alt="Website preview"
     style="max-width:600px;" />

Option 3: Dynamic URL (Real-Time Preview)

For the simplest approach, use the screenshot API URL directly as the image source:

<img src="https://hermesforge.dev/api/screenshot?url=https://example.com&width=600&height=400&format=png&block_ads=true"
     alt="Live preview of example.com"
     style="max-width:600px;" />

This generates a fresh screenshot every time the email is opened. Benefits: always current, no hosting needed. Drawbacks: depends on API availability at open time, may be slow for the first open.

Real-World Use Cases

Weekly Dashboard Reports

DASHBOARDS = [
    {"name": "Sales", "url": "https://app.yoursite.com/dashboard/sales"},
    {"name": "Traffic", "url": "https://app.yoursite.com/dashboard/traffic"},
    {"name": "Support", "url": "https://app.yoursite.com/dashboard/support"},
]

def build_weekly_report():
    html_parts = ["<h1>Weekly Dashboard Report</h1>"]

    for dash in DASHBOARDS:
        image = get_preview_image(dash["url"])
        if image:
            b64 = base64.b64encode(image).decode()
            html_parts.append(f"""
                <h2>{dash['name']}</h2>
                <a href="{dash['url']}">
                    <img src="data:image/png;base64,{b64}"
                         alt="{dash['name']} dashboard"
                         style="max-width:600px; border:1px solid #eee;" />
                </a>
            """)

    return "\n".join(html_parts)

New Content Notifications

When a user publishes a page, send a notification with a visual preview:

def send_publish_notification(page_url, author_email):
    preview_url = get_hosted_preview(page_url)

    html = f"""
    <p>Your page is live! Here's how it looks:</p>
    <a href="{page_url}">
        <img src="{preview_url}"
             alt="Your published page"
             style="max-width:600px; border:1px solid #ddd;" />
    </a>
    <p><a href="{page_url}">View your page →</a></p>
    """

    send_email(author_email, "Your page is published!", html)

Portfolio Showcase Emails

Show multiple websites in a grid layout:

<table cellpadding="10" style="width:100%;">
  <tr>
    <td style="width:50%;">
      <img src="https://hermesforge.dev/api/screenshot?url=https://client1.com&width=280&height=200&format=png"
           alt="Client 1" style="width:100%;" />
      <p>Client 1</p>
    </td>
    <td style="width:50%;">
      <img src="https://hermesforge.dev/api/screenshot?url=https://client2.com&width=280&height=200&format=png"
           alt="Client 2" style="width:100%;" />
      <p>Client 2</p>
    </td>
  </tr>
</table>

Tips for Email-Friendly Screenshots

  1. Width matters: Most email clients render at 600px max. Use width=600 or smaller.
  2. PNG over WebP: WebP isn't supported in all email clients. Stick with PNG for email.
  3. Keep file sizes reasonable: Full-page screenshots can be huge. Set specific dimensions instead of using full_page=true.
  4. Add alt text: Always include descriptive alt text for accessibility and clients that block images by default.
  5. Use block_ads=true: Clean previews look more professional in emails.
  6. Cache aggressively: If sending the same preview to many recipients, generate once and host.

Why Not Just Use a Thumbnail Service?

Thumbnail services like PagePeeker or ShrinkTheWeb exist, but they typically: - Return low-quality, small images - Cache aggressively (showing outdated content) - Require paid plans for custom dimensions - Don't support features like ad blocking or dark mode

A screenshot API gives you full control: exact dimensions, format choice, JavaScript execution, ad blocking, and real-time captures.

Every email with a website preview is a click opportunity. Make the preview look good, and the clicks follow.