Automate Dynamic OG Image Generation for Blog Posts, Product Pages, and User Profiles

Profile

Written By Hanzala Saleem

Updated At June 19, 2026 | 8 min read

Every time someone shares a link on Twitter, LinkedIn, or Slack, a preview image appears. That image either earns the click or gets scrolled past. Most sites ship the same static banner on every URL, so a blog post, a pricing page, and a user profile all share one generic image at the exact moment a unique one would do the most work.

Dynamic Open Graph images solve this. Instead of one image for the whole site, each page gets its own social preview, generated automatically at publish time.

This guide covers how dynamic OG image generation works, why self-hosting Puppeteer is a poor long-term choice for it, and how to automate the whole pipeline with a single API call using ScreenshotAPI.net.

image

What Are Dynamic Open Graph Images?

Open Graph is a protocol created by Facebook that controls how a URL looks when shared on social platforms. The og:image meta tag sets the preview image for that URL.

A static OG image is one file you set once in your site template. Every page shares it.

A dynamic OG image is generated per URL, usually as a 1200x630 PNG. It can carry that page's title, author, date, product photo, price, or anything else specific to the content.

Contextual previews tend to pull more clicks than a repeated generic banner, because a reader can see what the link is about before deciding to open it.

Why Automating OG Image Generation Matters

For a blog with five posts, making a custom OG image by hand is fine. For 500 posts, a 2,000-SKU catalog, or a SaaS app with a profile page per user, manual creation falls apart.

Automation fixes several concrete problems:

  • New content gets a real social preview the moment it publishes.
  • Product pages show accurate titles and pricing with no extra design work.
  • Profiles show the actual username and avatar instead of a house banner.
  • Nobody waits on a designer before every publish.

The target is a repeatable system where your CMS or backend produces the OG image as part of the publish pipeline, with no human in the loop.

The Problem With Using Puppeteer for OG Image Generation

The common DIY approach is to run a Puppeteer or Playwright instance on your server, load an HTML template, and screenshot it. It works in a demo. In production it creates steady friction.

Headless Chrome is memory-hungry. A common operational guideline is to budget 300 to 500 MB of RAM per instance, and under real load you run several at once. Memory leaks that never showed up in development become incidents in production. Chrome binary versions drift and need patching. You write and maintain custom logic for queuing, retries, and timeouts.

The hidden infrastructure cost stacks up fast:

  • Servers sized for concurrent headless browsers.
  • Engineering time to build and maintain a rendering service.
  • Debugging time for zombie processes and edge cases.
  • Routine Chrome CVE patching and version-compatibility work.

That is all overhead with no connection to your actual product.

A Better Approach: OG Image Generation via API

ScreenshotAPI.net runs managed Chromium rendering infrastructure. You send a request with your HTML template and parameters, and it returns a PNG. No browsers to install, no servers to scale, no zombie processes to chase.

The key parameter for OG images is custom_html. It lets you pass raw HTML directly instead of a URL, so you can build a pixel-perfect template in HTML and CSS, inject page-specific data, and render it to an image in one request.

The endpoint is:

GET  https://shot.screenshotapi.net/v3/screenshot?token=YOUR_TOKEN&[OPTIONS]
POST https://shot.screenshotapi.net/v3/screenshot   (use POST for long HTML)

For OG images you set width=1200, height=630, and output=image alongside your custom_html.

Send long templates via POST, not GET. A realistic OG template runs well past the character limit of a GET query string. ScreenshotAPI's own docs note that shorter HTML can go through GET, but longer HTML should be sent with POST. The GET examples below are kept short for readability. For production templates, move custom_html into a POST body and URL-encode it.

How to Generate OG Images for Blog Posts

You have a blog post and want a unique social preview at publish time.

  1. Build your OG template in HTML with placeholders for title, author, and date.
  2. At publish time, inject the post data into the template.
  3. Send the rendered HTML to ScreenshotAPI and store the returned image.

Node.js Example

const axios = require("axios");
const fs = require("fs");

async function generateBlogOGImage(title, author, date) {
  const html = `
    <html>
      <head>
        <style>
          body {
            margin: 0;
            width: 1200px;
            height: 630px;
            display: flex;
            align-items: center;
            justify-content: center;
            background: #0D0F14;
            font-family: sans-serif;
          }
          .card { padding: 60px; color: white; }
          h1 { font-size: 52px; margin: 0 0 24px; line-height: 1.2; }
          .meta { font-size: 22px; color: #a0a0b0; }
          .accent { color: #5628FB; font-size: 18px; margin-bottom: 16px; }
        </style>
      </head>
      <body>
        <div class="card">
          <div class="accent">My Blog</div>
          <h1>${title}</h1>
          <div class="meta">${author} &bull; ${date}</div>
        </div>
      </body>
    </html>
  `;

  // POST keeps you clear of GET query-string limits on real templates.
  const response = await axios.post(
    "https://shot.screenshotapi.net/v3/screenshot",
    {
      token: "YOUR_API_TOKEN",
      custom_html: html,
      width: 1200,
      height: 630,
      output: "image",
      file_type: "png",
    },
    { responseType: "arraybuffer" }
  );

  fs.writeFileSync("og-blog.png", response.data);
  console.log("OG image saved.");
}

generateBlogOGImage("How We Scaled to 1M Users", "Jane Doe", "June 2026");

One call returns a 1200x630 PNG, ready to store and reference in your og:image tag.

Generating OG Images for Product Pages

For e-commerce or SaaS product pages, you might want the product name, price, and category. Same approach: inject the product data into the template, then send it to the API.

Python Example

import requests

def generate_product_og_image(product_name, price, category):
    html = f"""
    <html>
      <head>
        <style>
          body {{
            margin: 0;
            width: 1200px;
            height: 630px;
            background: #ffffff;
            font-family: sans-serif;
            display: flex;
            align-items: center;
            padding: 60px;
            box-sizing: border-box;
          }}
          .label {{ color: #5628FB; font-size: 20px; margin-bottom: 12px; }}
          h1 {{ font-size: 56px; margin: 0 0 20px; color: #0D0F14; }}
          .price {{ font-size: 36px; color: #5628FB; font-weight: bold; }}
        </style>
      </head>
      <body>
        <div>
          <div class="label">{category}</div>
          <h1>{product_name}</h1>
          <div class="price">{price}</div>
        </div>
      </body>
    </html>
    """

    # POST avoids GET length limits for full templates.
    response = requests.post(
        "https://shot.screenshotapi.net/v3/screenshot",
        json={
            "token": "YOUR_API_TOKEN",
            "custom_html": html,
            "width": 1200,
            "height": 630,
            "output": "image",
            "file_type": "png",
        },
    )

    with open("og-product.png", "wb") as f:
        f.write(response.content)

generate_product_og_image("Wireless Noise Cancelling Headphones", "$149.00", "Audio")

Generating OG Images for User Profiles

Profile pages benefit from a personalized preview showing the username and role. The same custom_html parameter handles it with no infrastructure change. The cURL example below uses GET because the inline HTML is short. For a real profile template with avatars and longer markup, switch to POST.

cURL Example

curl -G "https://shot.screenshotapi.net/v3/screenshot" \
  --data-urlencode "token=YOUR_API_TOKEN" \
  --data-urlencode "custom_html=<html><body style='margin:0;width:1200px;height:630px;background:#0D0F14;display:flex;align-items:center;padding:60px;box-sizing:border-box;font-family:sans-serif;color:white;'><div><div style='color:#5628FB;font-size:20px;'>Community Member</div><h1 style='font-size:64px;margin:12px 0;'>@janedoe</h1><p style='color:#a0a0b0;font-size:24px;'>Full Stack Developer</p></div></body></html>" \
  --data-urlencode "width=1200" \
  --data-urlencode "height=630" \
  --data-urlencode "output=image" \
  --data-urlencode "file_type=png" \
  -o og-profile.png

Storing and Serving the Generated Images

Once the API returns the image, you have two options:

  • Save to your own storage with the byob=true parameter and a configured S3, Google Cloud, or Wasabi bucket. The image is delivered straight to your bucket without touching your server.
  • Catch the response in your backend and upload it to your CDN or object storage yourself.

Either way, the image URL goes into your database and into the og:image tag:

<meta property="og:image" content="https://your-cdn.com/og/blog-post-slug.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />

For the full spec, see the Open Graph protocol documentation.

Puppeteer vs ScreenshotAPI: A Direct Comparison

FactorSelf-hosted PuppeteerScreenshotAPI
SetupInstall Chrome, write a render service, wire up queuingOne HTTP request
Memory~300 to 500 MB RAM budgeted per instanceNone on your servers
ScalingYou provision and tune concurrencyHandled by the API
MaintenanceChrome version drift, CVE patches, zombie-process cleanupNone
Failure modesMemory leaks, timeouts, orphaned processesAPI error codes you handle in one place
StorageYou build itbyob to your bucket, or catch and upload
Time to first imageHours to daysMinutes

The trade-off is simple: Puppeteer gives you full control and zero per-call cost, which can win at very high volume or for unusual rendering needs. An API removes the operational surface entirely, which usually wins when OG images are a supporting feature rather than your core product.

Best Practices for Dynamic OG Image Automation

Keep the template simple. Complex layouts with external fonts and remote images slow renders and add failure points. Inline your CSS and prefer system fonts.

Cache what does not change. ScreenshotAPI supports an enable_caching parameter with a configurable TTL. For stable content, cached images speed up the pipeline and cut API calls.

Generate at publish time, not request time. Generating on share adds latency to that request. Create and store the image when content is first published.

Set the viewport precisely. Use width=1200 and height=630 on every OG request to match what Twitter, LinkedIn, and Facebook expect. retina=true produces a sharper 2400x1260 output if you want higher density.

Validate with platform debuggers. After publishing, check your images with the Twitter Card Validator and the Facebook Sharing Debugger to confirm they resolve correctly.

Frequently Asked Questions

What is dynamic OG image generation?

It is the process of automatically creating a unique Open Graph preview image for each page on a site. Instead of one static banner shared across every URL, each blog post, product page, or profile gets its own image built from a template plus page-specific data.

Can I generate OG images without running Puppeteer on my server?

Yes. ScreenshotAPI exposes a REST API that renders HTML to PNG on managed Chromium. You send your template and dimensions, and it returns the image. There is no browser to install and no rendering infrastructure to operate.

What size should an OG image be?

1200x630 pixels. This is the standard supported across Twitter, LinkedIn, and Facebook. In ScreenshotAPI, set width=1200 and height=630.

Should I call the API with GET or POST?

Use POST for real templates. GET query strings have length limits that a full OG template will exceed once you add styling and data. Short, demonstrative HTML can go through GET, but production code should send custom_html in a POST body.

How do I automate OG images for a CMS like WordPress or Ghost?

Trigger generation from a webhook when content publishes. The handler calls ScreenshotAPI with your template populated by the post's title, author, and metadata, stores the returned image in cloud storage, and saves the URL back to the post. ScreenshotAPI also connects to Zapier, Make, and n8n for a no-code path.