Generate Social Media Cards Automatically with a Screenshot API

2026-04-27 | Tags: [screenshot-api, social-media, og-image, automation]

Every link shared on Twitter, LinkedIn, Slack, or Discord shows a preview card. That card is usually generated from your page's og:image meta tag. If you don't have one, the platform either shows nothing or picks a random image from your page.

Most developers solve this by manually creating images in Figma or Canva. That doesn't scale. If you have a blog with 50 posts, a docs site with 200 pages, or a SaaS with dynamic user profiles, you need automated card generation.

The Screenshot Approach

Instead of building a custom image generation pipeline, you can screenshot a purpose-built HTML template. Here's how:

  1. Create an HTML page that renders your card design
  2. Pass dynamic data (title, description, author) via URL parameters
  3. Screenshot it at the right dimensions (1200x630 for Open Graph)
curl "https://hermesforge.dev/api/screenshot?url=https://yoursite.com/og-card?title=My+Post&width=1200&height=630&format=webp"

Why This Works Better Than You'd Think

It's just HTML and CSS. You already know how to style a card. You don't need to learn Canvas, Sharp, Puppeteer internals, or a template language. Write CSS, get an image.

It handles dynamic content naturally. User avatars, variable-length titles, custom colors — all trivial in CSS, painful in image generation libraries.

WebP output keeps files small. A 1200x630 social card in WebP is typically 30-50KB. Fast to serve, fast to load.

A Minimal Card Template

<!DOCTYPE html>
<html>
<head>
<style>
  body {
    margin: 0;
    width: 1200px;
    height: 630px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 60px;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    font-family: system-ui, sans-serif;
    color: white;
    box-sizing: border-box;
  }
  h1 { font-size: 56px; margin: 0 0 20px 0; line-height: 1.2; }
  p { font-size: 24px; opacity: 0.9; margin: 0; }
  .footer { margin-top: auto; font-size: 20px; opacity: 0.7; }
</style>
</head>
<body>
  <h1>Your Post Title Here</h1>
  <p>A brief description of what this post covers</p>
  <div class="footer">yoursite.com</div>
</body>
</html>

Host this template, pass the title and description as query parameters, and screenshot it. Done.

Automating at Build Time

If you use a static site generator (Next.js, Hugo, Astro), you can generate cards during the build:

import requests

posts = get_all_posts()  # your CMS/markdown reader

for post in posts:
    params = {
        "url": f"https://yoursite.com/og-template?title={post.title}",
        "width": 1200,
        "height": 630,
        "format": "webp",
        "quality": 85
    }
    resp = requests.get("https://hermesforge.dev/api/screenshot", params=params)
    with open(f"public/og/{post.slug}.webp", "wb") as f:
        f.write(resp.content)

Then reference the generated image in your HTML:

<meta property="og:image" content="https://yoursite.com/og/my-post-slug.webp" />

Dynamic Generation with Caching

For sites where content changes frequently, generate cards on-demand and cache them:

from flask import Flask, send_file, request
import requests
import hashlib
import os

app = Flask(__name__)
CACHE_DIR = "/tmp/og-cache"

@app.route("/og/<path:slug>.png")
def og_image(slug):
    cache_key = hashlib.md5(slug.encode()).hexdigest()
    cache_path = os.path.join(CACHE_DIR, f"{cache_key}.webp")

    if not os.path.exists(cache_path):
        resp = requests.get(
            "https://hermesforge.dev/api/screenshot",
            params={
                "url": f"https://yoursite.com/og-template?slug={slug}",
                "width": 1200, "height": 630,
                "format": "webp"
            }
        )
        os.makedirs(CACHE_DIR, exist_ok=True)
        with open(cache_path, "wb") as f:
            f.write(resp.content)

    return send_file(cache_path, mimetype="image/webp")

Platform-Specific Dimensions

Different platforms expect different sizes:

Platform Recommended Size Aspect Ratio
Twitter/X 1200 × 628 ~1.91:1
LinkedIn 1200 × 627 ~1.91:1
Facebook 1200 × 630 ~1.91:1
Discord 1200 × 630 ~1.91:1
Slack 800 × 418 ~1.91:1

Good news: 1200×630 works well everywhere. Use that as your default.

The No-Signup Option

You don't need an API key to try this. The screenshot API at hermesforge.dev works without authentication for basic usage. Just pass a URL and get an image back.

For production use with higher rate limits, get a free API key — it takes 10 seconds.

Why Not Use a Dedicated OG Image Service?

Services like Cloudinary and Imgix offer OG image generation. They work, but:

The screenshot approach trades some performance (generating an image takes 2-3 seconds vs. milliseconds for a template engine) for maximum flexibility. Cache the results and the performance difference disappears.