Build an Automatic Social Media Preview Generator
Published February 20, 2026 ยท 8 min read
Photo via Unsplash
When someone shares your link on Twitter, LinkedIn, or Slack, the preview image (og:image) makes or breaks the click. A boring default image? Ignored. A custom, dynamic preview showing the actual content? That gets clicks.
Building an OG image generator API used to require complex canvas rendering or Puppeteer servers. With a screenshot API, you can design OG images in HTML/CSS (which you already know) and convert them to images instantly.
๐ TL;DR: Design your social preview cards in HTML, host them as templates, and use SnapAPI to render them as images on-the-fly. Dynamic OG images for every page, zero design tools needed.
How It Works
The approach is simple and powerful:
- Create an HTML template for your OG image (1200ร630px for most platforms)
- Pass dynamic data via URL parameters (title, author, date, etc.)
- Screenshot the template using SnapAPI to generate the image
- Serve it as your og:image in meta tags
Step 1: Design the HTML Template
Create a simple HTML page that renders your OG image. Host it on your server or use SnapAPI's HTML rendering feature:
<!-- og-template.html -->
<html>
<body style="margin:0; width:1200px; height:630px; display:flex;
align-items:center; justify-content:center;
background: linear-gradient(135deg, #0f0f1a, #1a1a2e);
font-family: -apple-system, sans-serif;">
<div style="text-align:center; padding:60px;">
<div style="font-size:20px; color:#00d4ff;
text-transform:uppercase; letter-spacing:3px;
margin-bottom:24px;">
{{category}}
</div>
<h1 style="font-size:52px; color:white;
line-height:1.2; margin:0 0 24px;">
{{title}}
</h1>
<p style="font-size:20px; color:#94a3b8;">
{{author}} ยท {{date}}
</p>
<div style="margin-top:32px;">
<img src="https://snapapi.pics/logo-translucent-128.png"
width="40" height="40" />
</div>
</div>
</body>
</html>
Step 2: Render with SnapAPI
Option A: Screenshot a hosted template URL
curl -X POST https://api.snapapi.pics/v1/screenshot \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yoursite.com/og-template?title=My+Blog+Post&author=Jane+Doe&date=Feb+2026&category=Tutorial",
"width": 1200,
"height": 630,
"format": "png"
}' --output og-image.png
Option B: Render raw HTML directly
curl -X POST https://api.snapapi.pics/v1/screenshot \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"html": "<html><body style=\"margin:0;width:1200px;height:630px;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#0f0f1a,#1a1a2e);font-family:sans-serif\"><h1 style=\"color:white;font-size:48px;text-align:center;padding:40px\">My Amazing Blog Post</h1></body></html>",
"width": 1200,
"height": 630,
"format": "png"
}' --output og-image.png
Node.js โ Dynamic OG Image Server
const express = require('express');
const fetch = require('node-fetch');
const app = express();
const SNAPAPI_KEY = process.env.SNAPAPI_KEY;
const OG_CACHE = new Map();
app.get('/og/:slug', async (req, res) => {
const { slug } = req.params;
// Check cache (OG images don't change often)
if (OG_CACHE.has(slug)) {
res.set('Content-Type', 'image/png');
res.set('Cache-Control', 'public, max-age=86400');
return res.send(OG_CACHE.get(slug));
}
// Fetch post data from your CMS/database
const post = await db.getPost(slug);
if (!post) return res.status(404).send('Not found');
// Build template URL with dynamic data
const templateUrl = new URL('https://yoursite.com/og-template.html');
templateUrl.searchParams.set('title', post.title);
templateUrl.searchParams.set('author', post.author);
templateUrl.searchParams.set('date', post.date);
templateUrl.searchParams.set('category', post.category);
// Capture screenshot
const response = await fetch('https://api.snapapi.pics/v1/screenshot', {
method: 'POST',
headers: {
'X-API-Key': SNAPAPI_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: templateUrl.toString(),
width: 1200,
height: 630,
format: 'png'
})
});
const buffer = Buffer.from(await response.arrayBuffer());
OG_CACHE.set(slug, buffer);
res.set('Content-Type', 'image/png');
res.set('Cache-Control', 'public, max-age=86400');
res.send(buffer);
});
app.listen(3000);
Python โ Flask OG Image Generator
from flask import Flask, send_file, abort
from io import BytesIO
import requests
import os
app = Flask(__name__)
SNAPAPI_KEY = os.environ['SNAPAPI_KEY']
cache = {}
@app.route('/og/<slug>')
def generate_og(slug):
if slug in cache:
return send_file(
BytesIO(cache[slug]),
mimetype='image/png'
)
# Get post data from your database
post = db.get_post(slug)
if not post:
abort(404)
# Build HTML for the OG image
html = f"""
<html>
<body style="margin:0;width:1200px;height:630px;display:flex;
align-items:center;justify-content:center;
background:linear-gradient(135deg,#0f0f1a,#1a1a2e);
font-family:sans-serif">
<div style="text-align:center;padding:60px">
<div style="color:#00d4ff;font-size:18px;
text-transform:uppercase;letter-spacing:3px;
margin-bottom:20px">{post['category']}</div>
<h1 style="color:white;font-size:48px;
line-height:1.2;margin:0">{post['title']}</h1>
<p style="color:#94a3b8;font-size:18px;
margin-top:20px">{post['author']} ยท {post['date']}</p>
</div>
</body>
</html>
"""
response = requests.post(
'https://api.snapapi.pics/v1/screenshot',
headers={
'X-API-Key': SNAPAPI_KEY,
'Content-Type': 'application/json'
},
json={
'html': html,
'width': 1200,
'height': 630,
'format': 'png'
}
)
cache[slug] = response.content
return send_file(BytesIO(response.content), mimetype='image/png')
Step 3: Add to Your Meta Tags
Point your og:image to your OG image endpoint:
<!-- In your page's <head> -->
<meta property="og:image" content="https://yoursite.com/og/my-blog-post" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="https://yoursite.com/og/my-blog-post" />
Advanced: Template Variations
Create different templates for different content types:
- Blog posts: Title + author + reading time on gradient background
- Product pages: Product screenshot + price + rating
- User profiles: Avatar + name + stats
- Documentation: Code snippet preview + section title
- Changelog: Version number + key features list
Performance & Caching Tips
- Cache aggressively: OG images rarely change. Cache for 24 hours minimum.
- Pre-generate on publish: Generate OG images when content is published, not on first request.
- Use a CDN: Store generated images on S3/CloudFlare R2 and serve via CDN.
- JPEG for photos: If your template includes photos, use JPEG format (smaller files).
- PNG for text/graphics: For text-heavy templates, PNG gives crisper results.
Platform-Specific Sizes
- Facebook/LinkedIn: 1200ร630px (1.91:1 ratio)
- Twitter: 1200ร628px (summary_large_image) or 800ร418px
- Pinterest: 1000ร1500px (2:3 ratio)
- Slack: Uses og:image, same as Facebook
Pricing
OG image generation is extremely cost-effective with SnapAPI:
- Free: 200 images/month โ perfect for small blogs
- Pro ($19/mo): 25,000 images โ handles thousands of pages
- Business ($79/mo): 100,000 images โ for large content platforms
With caching, even a site with millions of pages only needs to generate each image once.
Get Started
Stop using boring default OG images. Sign up for free and start generating dynamic social previews in minutes.
๐ก Pro tip: Test your OG images with Facebook's Sharing Debugger and Twitter's Card Validator to ensure they render correctly on each platform.