Generate Email-Safe Website Previews with a Screenshot API
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).
Option 2: Host and Link
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
- Width matters: Most email clients render at 600px max. Use
width=600or smaller. - PNG over WebP: WebP isn't supported in all email clients. Stick with PNG for email.
- Keep file sizes reasonable: Full-page screenshots can be huge. Set specific dimensions instead of using
full_page=true. - Add alt text: Always include descriptive alt text for accessibility and clients that block images by default.
- Use
block_ads=true: Clean previews look more professional in emails. - 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.