Use Case

Social Media Preview Generator API

Tutorial Social Media

Build an Automatic Social Media Preview Generator

Published February 20, 2026 · 8 min read

Social media apps on phone screen

Photo by Dole777 on 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.

Start Capturing for Free

200 screenshots/month. Screenshots, PDF, scraping, and video recording. No credit card required.

Get Free API Key →

Advanced OG Image Generation Patterns

Social media previews (Open Graph images) are critical for click-through rates. A well-designed OG image can increase link clicks by 40-60% on Twitter/X and LinkedIn. Here is how to generate them programmatically at scale.

Dynamic OG Images with HTML Templates (Node.js)

const express = require('express');
const fetch = require('node-fetch');
const app = express();

// Template endpoint that renders dynamic HTML
app.get('/og-template', (req, res) => {
  const { title, desc, author } = req.query;
  res.send(`
    <html><head>
      <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@700;800&display=swap">
      <style>
        body { margin: 0; background: #0f172a; font-family: Inter, sans-serif;
               display: flex; align-items: center; justify-content: center; height: 630px; }
        .card { width: 1200px; height: 630px; background: linear-gradient(135deg, #1e1b4b, #0f172a);
                border: 2px solid #3730a3; border-radius: 16px; padding: 60px; box-sizing: border-box; }
        h1 { font-size: 52px; font-weight: 800; color: #f8fafc; margin: 0 0 20px; line-height: 1.2; }
        p  { font-size: 24px; color: #94a3b8; margin: 0 0 40px; }
        .author { font-size: 18px; color: #a78bfa; }
      </style>
    </head><body>
      <div class="card">
        <h1>${decodeURIComponent(title)}</h1>
        <p>${decodeURIComponent(desc)}</p>
        <div class="author">${decodeURIComponent(author)}</div>
      </div>
    </body></html>
  `);
});

// Generate OG image via SnapAPI
async function generateOgImage(title, desc, author) {
  const templateUrl = encodeURIComponent(
    'http://localhost:3000/og-template?' +
    'title=' + encodeURIComponent(title) +
    '&desc=' + encodeURIComponent(desc) +
    '&author=' + encodeURIComponent(author)
  );

  const response = await fetch(
    'https://api.snapapi.pics/v1/screenshot?format=png&width=1200&height=630&url=' + templateUrl,
    { headers: { 'X-API-Key': 'YOUR_KEY' } }
  );
  return response.buffer(); // Returns PNG buffer ready to upload to CDN
}

app.listen(3000);

Bulk OG Generation for Blog Posts (Python)

import requests, json, asyncio, aiohttp
from pathlib import Path

SNAPAPI_KEY = "YOUR_KEY"
BLOG_POSTS = [
    {"slug": "puppeteer-alternative", "title": "Best Puppeteer Alternative 2026", "date": "Apr 2026"},
    {"slug": "wkhtmltopdf-migration", "title": "Migrating from wkhtmltopdf", "date": "Apr 2026"},
    {"slug": "screenshot-api-guide",  "title": "Screenshot API Complete Guide", "date": "Apr 2026"},
]

async def generate_og(session, post):
    params = {"url": f"https://snapapi.pics/blog/{post['slug']}", "format": "png",
              "width": 1200, "height": 630, "full_page": False}
    async with session.get("https://api.snapapi.pics/v1/screenshot",
                           headers={"X-API-Key": SNAPAPI_KEY}, params=params) as r:
        data = await r.read()
        Path(f"og-images/{post['slug']}.png").write_bytes(data)
        print(f"Generated OG: {post['slug']}.png")

async def main():
    async with aiohttp.ClientSession() as session:
        await asyncio.gather(*[generate_og(session, p) for p in BLOG_POSTS])

asyncio.run(main())

OG Image Best Practices

Size: 1200x630px

The standard OG image size. Twitter renders at 1200x628, LinkedIn at 1200x627. Use 1200x630 to cover all platforms.

Cache the PNG

Generate once, store on S3 or Cloudflare R2. Serve from CDN. OG images do not need to be dynamic on every request — generate on publish.

Keep Text Large

Title text should be 48px minimum. Descriptions at 24px. Twitter shows a 300x157px thumbnail in feeds — small text becomes unreadable.

High Contrast Colors

Dark backgrounds with white or bright accent text. Avoid pastel gradients — they look washed out in both dark and light social UI themes.

FAQ

Can I screenshot my own localhost templates?

Not directly, since SnapAPI runs on our servers. Use ngrok or a staging URL to expose your template endpoint, or pass raw HTML using the html parameter.

What is the typical generation time?

Simple HTML templates: 400-800ms. Pages that load external fonts or images: 1-2s. Add a 2s delay param for Google Fonts to load before capture.

Can I generate WebP instead of PNG?

Yes — set format=webp. WebP is 25-35% smaller than PNG at the same quality, which helps with CDN storage costs on large image libraries.

Generate Social Media Previews at Scale

200 free screenshots per month. PNG, JPEG, or WebP. Start in minutes.

Start Free Read Docs