Back to Blog
Guide February 23, 2026 · 4 min read

How to Generate Dynamic Open Graph Images with an API

Generate dynamic OG images for blog posts, products, and dashboards with a single API call. No serverless function, no headless browser to maintain.

Every link you share on Twitter, Slack, or LinkedIn pulls an OG image. If that image is a static fallback — or worse, missing — you're leaving clicks on the table.

Dynamic OG images (unique per page, showing the actual title/author/stats) consistently outperform generic logos. The problem is generating them at scale without spinning up a Puppeteer instance or a Vercel Edge Function.

Here's the straightforward approach: an API call that returns a PNG.

The API call

const response = await fetch('https://api.pagebolt.dev/v1/og-image', {
  method: 'POST',
  headers: {
    'x-api-key': 'YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    title: 'How to deploy a Node.js app to Fly.io in 5 minutes',
    subtitle: 'Marcus Chen · 8 min read',
    template: 'gradient',
    accentColor: '#6366f1'
  })
});

// Returns a PNG — save it or stream it directly
const buffer = Buffer.from(await response.arrayBuffer());
fs.writeFileSync('og-image.png', buffer);

Three built-in templates: default, minimal, gradient. Pass accentColor to match your brand. The result is a 1200×630 PNG, ready to drop into your <meta property="og:image"> tag.

Generating per-post at build time

For a static site (Next.js, Astro, Eleventy), generate OG images during your build:

// generate-og-images.js — run as part of your build
const posts = await getAllPosts(); // your CMS or markdown files

for (const post of posts) {
  const res = await fetch('https://api.pagebolt.dev/v1/og-image', {
    method: 'POST',
    headers: { 'x-api-key': process.env.PAGEBOLT_API_KEY },
    body: JSON.stringify({
      title: post.title,
      subtitle: `${post.author} · ${post.readingTime} min read`,
      template: 'gradient',
      accentColor: '#6366f1'
    })
  });

  const buffer = Buffer.from(await res.arrayBuffer());
  fs.writeFileSync(`public/og/${post.slug}.png`, buffer);
}

Then reference it in your HTML:

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

On-demand generation (API route)

For dynamic pages — user profiles, dashboards, product listings — generate on request and cache:

// pages/api/og/[slug].js (Next.js)
export default async function handler(req, res) {
  const { slug } = req.query;
  const post = await getPost(slug);

  const ogRes = await fetch('https://api.pagebolt.dev/v1/og-image', {
    method: 'POST',
    headers: { 'x-api-key': process.env.PAGEBOLT_API_KEY },
    body: JSON.stringify({ title: post.title, subtitle: post.author, template: 'minimal' })
  });

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

  res.setHeader('Content-Type', 'image/png');
  res.setHeader('Cache-Control', 'public, max-age=86400, stale-while-revalidate=604800');
  res.send(buffer);
}

The Cache-Control header means the image is generated once and cached at the CDN edge — you pay one API call per unique slug, not per share.

Custom HTML template

Need full control over the layout? Pass raw HTML instead:

body: JSON.stringify({
  html: `<div style="background:#0f172a;color:white;padding:60px;font-family:Inter,sans-serif;width:1200px;height:630px;">
    <h1 style="font-size:56px;">${post.title}</h1>
    <p style="color:#94a3b8;">${post.author}</p>
  </div>`
})

The API renders your HTML in a 1200×630 viewport and returns the screenshot as a PNG. Any CSS works — gradients, images, custom fonts via Google Fonts.


Dynamic OG images are one of those high-ROI details that most teams skip because the tooling feels heavy. One fetch call removes that excuse.

Try it free — 100 requests/month

No credit card. No headless browser to manage. Just an API key.