DEV Community

Custodia-Admin
Custodia-Admin

Posted on • Originally published at pagebolt.dev

How to generate dynamic Open Graph images with an API (no Puppeteer, no Vercel Edge)

How to Generate Dynamic Open Graph Images with an API

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);
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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" />
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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>`
})
Enter fullscreen mode Exit fullscreen mode

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. → pagebolt.dev

Top comments (0)