Generate Social Media Preview Cards with Screenshot API
March 2026 -- 8 min read
When someone shares a link on Twitter, LinkedIn, Slack, or Discord, those platforms display a preview card with a title, description, and image. The image is the most impactful element -- it determines whether people click. This guide shows you how to generate those images dynamically using a screenshot API.
Why Dynamic Social Cards Matter
Links with preview images get 2-3x more engagement than links without them. But manually creating an image for every page is impractical when you have hundreds or thousands of pages. Dynamic generation solves this.
Use cases for dynamic social cards:
- Blog posts -- generate a card showing the article title and layout
- E-commerce products -- show the product page as a preview
- User profiles -- capture a styled profile card
- Documentation pages -- auto-generate cards for API docs
- SaaS dashboards -- create shareable report snapshots
The Architecture
The approach is straightforward:
- Create an HTML template for your social card (or use the actual page)
- Use a screenshot API to capture it at 1200x630 pixels (the standard OG image size)
- Serve the image via your OG meta tags
- Cache the result for performance
Method 1: Screenshot Your Actual Pages
The simplest approach -- capture a screenshot of each page and resize to OG dimensions.
const express = require('express');
const app = express();
const API_BASE = 'https://screenshotapi-api-production.up.railway.app';
const API_KEY = process.env.SCREENSHOT_API_KEY;
// Cache for generated images (use Redis in production)
const cache = new Map();
app.get('/og-image', async (req, res) => {
const { url } = req.query;
if (!url) return res.status(400).send('URL required');
// Check cache
if (cache.has(url)) {
res.set('Content-Type', 'image/jpeg');
return res.send(cache.get(url));
}
// Generate screenshot at OG image dimensions
const params = new URLSearchParams({
url,
width: '1200',
height: '630',
format: 'jpeg',
quality: '85',
output_width: '1200', // Ensure exact output size
output_height: '630',
});
const response = await fetch(
`${API_BASE}/v1/screenshot?${params}`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
);
const buffer = Buffer.from(await response.arrayBuffer());
// Cache for 1 hour
cache.set(url, buffer);
setTimeout(() => cache.delete(url), 3600000);
res.set('Content-Type', 'image/jpeg');
res.set('Cache-Control', 'public, max-age=3600');
res.send(buffer);
});
app.listen(3000);Method 2: Custom Social Card Template
For more control, create a dedicated HTML template for your social cards and screenshot that instead.
// app/social-card/page.js
// A dedicated page designed to look good at 1200x630
export default function SocialCard({ searchParams }) {
const { title, description, author } = searchParams;
return (
<div style={{
width: 1200, height: 630,
display: 'flex', flexDirection: 'column',
justifyContent: 'center', padding: 80,
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
color: 'white', fontFamily: 'Inter, sans-serif',
}}>
<h1 style={{ fontSize: 48, fontWeight: 800, marginBottom: 24 }}>
{title || 'My Blog Post'}
</h1>
<p style={{ fontSize: 24, opacity: 0.9 }}>
{description || 'A great article about something interesting'}
</p>
<div style={{ marginTop: 'auto', fontSize: 18, opacity: 0.7 }}>
{author || 'yourdomain.com'}
</div>
</div>
);
}// Use CSS injection to ensure no scrollbars
const params = new URLSearchParams({
url: 'https://yoursite.com/social-card?title=My+Post&author=John',
width: '1200',
height: '630',
format: 'jpeg',
quality: '90',
css: 'body { margin: 0; overflow: hidden; }',
wait: '500',
});
const response = await fetch(
`https://screenshotapi-api-production.up.railway.app/v1/screenshot?${params}`,
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);Method 3: Thumbnail Previews
For link directories or bookmark managers, generate smaller thumbnails:
# Capture at full viewport, resize output to 400px wide thumbnail
curl "https://screenshotapi-api-production.up.railway.app/v1/screenshot\
?url=https://example.com\
&width=1280&height=800\
&output_width=400\
&format=webp&quality=80" \
-H "Authorization: Bearer YOUR_API_KEY" \
-o thumbnail.webpAdding OG Meta Tags
Once you have your image generation endpoint, reference it in your HTML:
<!-- Open Graph (Facebook, LinkedIn) -->
<meta property="og:image" content="https://yoursite.com/og-image?url=https://yoursite.com/blog/my-post" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="https://yoursite.com/og-image?url=https://yoursite.com/blog/my-post" />Performance Tips
- Cache aggressively: Social card images rarely change. Cache for at least 1 hour, ideally 24 hours.
- Use a CDN: Put Cloudflare or similar in front of your image endpoint.
- Use WebP format: Smaller file sizes, supported by all major platforms.
- Pre-generate on publish: Generate the image when content is published, not on first request.
- Use output_width/output_height: Resize on the server instead of capturing at small viewport sizes, which can break responsive layouts.
Conclusion
Dynamic social card generation with a screenshot API is one of the highest-impact things you can do for link engagement. With the output resize feature, you can capture pages at their natural viewport size and resize to the exact dimensions social platforms expect -- no distorted layouts or broken responsive designs.
Generate Social Cards Now
Try the playground -- capture and resize screenshots instantly.
Open PlaygroundRelated Articles
OG Image Generator API
Generate Open Graph images dynamically from any HTML template.
Website Thumbnail Generation
Create website thumbnails with the output resize feature.
Build Link Previews
Add rich link previews to your app with screenshot thumbnails.
Automate Screenshots with Node.js
Build automated screenshot workflows with Node.js.