Guide OG Images

Generate Dynamic Open Graph Images with a Screenshot API

February 9, 2026 ยท 6 min read

Instagram social media app open on smartphone

Photo via Unsplash

When someone shares your page on Twitter, LinkedIn, Slack, or Discord, the link preview image is the first thing people see. A well-designed Open Graph image can dramatically increase click-through rates. But creating unique OG images for every blog post, product page, or user profile manually is impractical. The solution: generate them dynamically from HTML templates using a screenshot API.

What Are Open Graph Images?

Open Graph (OG) images are the preview images that appear when a URL is shared on social platforms. They are defined using meta tags in your HTML:

<meta property="og:image" content="https://yoursite.com/og/my-post.png">
<meta property="og:title" content="My Blog Post Title">
<meta property="og:description" content="A short description of the post">

The recommended dimensions are 1200x630 pixels for maximum compatibility across all platforms. Without a custom OG image, platforms either show a generic icon or a random image from your page, which looks unprofessional and lowers engagement.

Why Dynamic OG Images Matter

The Approach: HTML Template to Image

The workflow is simple:

  1. Create an HTML/CSS template for your OG image design
  2. Inject dynamic data (title, author, date, etc.) into the template
  3. Send the HTML to SnapAPI, which renders it in a real browser and returns a PNG
  4. Serve or cache the resulting image

Key advantage: Since SnapAPI renders HTML in a real Chromium browser, you get full CSS support -- gradients, custom fonts, flexbox, grid, shadows, and even background images all work perfectly.

Step 1: Design Your HTML Template

Here is a clean, reusable OG image template. Note the exact 1200x630 dimensions:

const template = (title, author, date) => `
<!DOCTYPE html>
<html>
<head>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      width: 1200px;
      height: 630px;
      display: flex;
      flex-direction: column;
      justify-content: center;
      padding: 80px;
      background: linear-gradient(135deg, #0a0a1a 0%, #1a0a2e 50%, #0a1a2e 100%);
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      color: white;
    }
    .logo {
      font-size: 24px;
      font-weight: 800;
      margin-bottom: 40px;
      opacity: 0.7;
    }
    .logo span { color: #00d4ff; }
    h1 {
      font-size: 56px;
      font-weight: 800;
      line-height: 1.2;
      margin-bottom: 30px;
      background: linear-gradient(135deg, #ffffff, #94a3b8);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
    }
    .meta {
      font-size: 20px;
      color: #64748b;
    }
    .meta span { color: #8b5cf6; }
    .corner {
      position: absolute;
      bottom: 40px;
      right: 80px;
      width: 60px;
      height: 60px;
      border-radius: 12px;
      background: linear-gradient(135deg, #00d4ff, #8b5cf6);
    }
  </style>
</head>
<body>
  <div class="logo">Snap<span>API</span> Blog</div>
  <h1>${title}</h1>
  <div class="meta">By <span>${author}</span> &middot; ${date}</div>
  <div class="corner"></div>
</body>
</html>
`;

Step 2: Generate the Image with Node.js

Send the HTML template to SnapAPI using the html parameter instead of url:

const fs = require("fs");

const SNAPAPI_KEY = "your_api_key";

async function generateOgImage(title, author, date) {
  const html = template(title, author, date);

  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({
      html: html,
      format: "png",
      width: 1200,
      height: 630,
      responseType: "image",
    }),
  });

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  const buffer = Buffer.from(await response.arrayBuffer());
  return buffer;
}

// Generate and save
async function main() {
  const image = await generateOgImage(
    "How to Build a Screenshot API",
    "SnapAPI Team",
    "February 9, 2026"
  );

  fs.writeFileSync("og-image.png", image);
  console.log("OG image generated: og-image.png");
}

main();

Step 3: Generate with Python

The same approach works in Python:

import requests

SNAPAPI_KEY = "your_api_key"

def generate_og_image(title: str, author: str, date: str) -> bytes:
    html = f"""
    <!DOCTYPE html>
    <html>
    <head>
      <style>
        * {{ margin: 0; padding: 0; box-sizing: border-box; }}
        body {{
          width: 1200px;
          height: 630px;
          display: flex;
          flex-direction: column;
          justify-content: center;
          padding: 80px;
          background: linear-gradient(135deg, #0a0a1a 0%, #1a0a2e 50%, #0a1a2e 100%);
          font-family: -apple-system, BlinkMacSystemFont, sans-serif;
          color: white;
        }}
        .logo {{ font-size: 24px; font-weight: 800; margin-bottom: 40px; opacity: 0.7; }}
        .logo span {{ color: #00d4ff; }}
        h1 {{
          font-size: 56px;
          font-weight: 800;
          line-height: 1.2;
          margin-bottom: 30px;
        }}
        .meta {{ font-size: 20px; color: #64748b; }}
        .meta span {{ color: #8b5cf6; }}
      </style>
    </head>
    <body>
      <div class="logo">Snap<span>API</span> Blog</div>
      <h1>{title}</h1>
      <div class="meta">By <span>{author}</span> &middot; {date}</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,
            "format": "png",
            "width": 1200,
            "height": 630,
            "responseType": "image",
        },
        timeout=30,
    )
    response.raise_for_status()
    return response.content


# Generate for a blog post
image = generate_og_image(
    "Building AI Agents with Web Data",
    "SnapAPI Team",
    "February 9, 2026"
)

with open("og-image.png", "wb") as f:
    f.write(image)

print(f"Generated OG image: {len(image)} bytes")

Serving OG Images Dynamically

In a real application, you want to serve OG images on demand. Here is an Express.js endpoint that generates and caches OG images:

const express = require("express");
const crypto = require("crypto");
const fs = require("fs");
const path = require("path");

const app = express();
const CACHE_DIR = "./og-cache";
const SNAPAPI_KEY = "your_api_key";

// Ensure cache directory exists
fs.mkdirSync(CACHE_DIR, { recursive: true });

app.get("/og/:slug.png", async (req, res) => {
  const { slug } = req.params;

  // Check cache first
  const cacheKey = crypto.createHash("md5").update(slug).digest("hex");
  const cachePath = path.join(CACHE_DIR, `${cacheKey}.png`);

  if (fs.existsSync(cachePath)) {
    res.setHeader("Content-Type", "image/png");
    res.setHeader("Cache-Control", "public, max-age=86400");
    return res.sendFile(path.resolve(cachePath));
  }

  // Look up post data from your database
  const post = await getPostBySlug(slug);
  if (!post) return res.status(404).send("Not found");

  // Generate OG image
  const html = template(post.title, post.author, post.date);

  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({
      html,
      format: "png",
      width: 1200,
      height: 630,
      responseType: "image",
    }),
  });

  if (!response.ok) {
    return res.status(500).send("Failed to generate image");
  }

  const buffer = Buffer.from(await response.arrayBuffer());

  // Write to cache
  fs.writeFileSync(cachePath, buffer);

  // Serve the image
  res.setHeader("Content-Type", "image/png");
  res.setHeader("Cache-Control", "public, max-age=86400");
  res.send(buffer);
});

app.listen(3000, () => console.log("OG image server on :3000"));

Then reference it in your HTML:

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

Caching Strategies

Generating OG images on every request is wasteful. Here are three proven caching approaches:

1. Build-Time Generation

Generate images during your build step (e.g., in a Next.js getStaticProps or a custom build script). Best for static sites where content changes infrequently.

2. On-Demand + File Cache

Generate on first request, save to disk, and serve from cache on subsequent requests (as shown in the Express example above). Good for sites with moderate traffic.

3. CDN Edge Caching

Put your OG image endpoint behind a CDN like Cloudflare or AWS CloudFront. Set long Cache-Control headers so the CDN caches the image at the edge. Best for high-traffic sites.

// Set headers for CDN caching
res.setHeader("Cache-Control", "public, max-age=604800, s-maxage=2592000");
res.setHeader("CDN-Cache-Control", "max-age=2592000");

Pro tip: Use AVIF format instead of PNG for 50% smaller files. Just change format: "png" to format: "avif". Most social platforms now support AVIF, and the smaller file size means faster page loads when platforms fetch your OG image.

Template Ideas

Here are some common OG image template patterns you can build:

Since you are writing full HTML/CSS, the design possibilities are unlimited. You can use Google Fonts (via <link> tag), SVG icons, background images, and any CSS feature supported by Chromium.

Testing Your OG Images

After implementing, test your OG images with these tools:

Next Steps

Try SnapAPI Free

Get 200 free screenshots per month. No credit card required.

Get Started Free →

Last updated: February 19, 2026