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.