OG Image Generator API — Automate Open Graph Images
Every link shared on social media gets a preview card. A title, a short description, and an image. That image is the Open Graph image, and it controls whether someone scrolls past or clicks through. Posts with a proper OG image consistently pull more clicks than posts with a broken preview or a stretched favicon. The difference is visible in any Slack channel or Twitter feed.
The problem starts when you need more than one. A blog with 80 articles, a SaaS with 200 feature pages, an e-commerce store with 500 products. Each page needs its own branded card. Building each one manually in Figma is fine for a launch week. After that, OG images become the task nobody picks up. Your links end up looking like everyone else's gray rectangles.
An OG image generator API turns this into a single HTTP request. You design one HTML template, inject the page's title and metadata, and the API renders it into a pixel-perfect PNG at 1200x630. You skip the browser stack, the design queue, and the manual export step entirely.
Why OG image generation breaks at scale
Most teams try one of three paths before they look for an open graph image API. All three work at small scale. All three fall apart when the page count grows.
Manual design in Figma or Canva means opening the editor, swapping in a new title and background, exporting as PNG, uploading, and updating the meta tag. Repeat for every new page. At two articles a week, you accumulate 100+ OG images in a year. Most teams stop maintaining them after the first month, and stale images pile up. Every article title change means re-exporting the image, and nobody remembers to do that.
Vercel's @vercel/og library (built on Satori) converts JSX to SVG without launching a browser. Fast and free, but Satori only handles a narrow slice of CSS. Anything beyond basic Flexbox — Grid, box-shadow, overlapping positioned elements, gradient text — gets silently dropped or breaks the render. Debugging is rough too: cryptic errors, no local preview, and deploying to test every change. It also locks you into the Vercel ecosystem.
Self-hosted Puppeteer or Playwright gives you full CSS support, but the infrastructure tax is real. Running your own headless browser means allocating server resources to Chrome processes, building a task queue, and keeping fonts consistent across environments. A senior developer can spend two or three days building this pipeline, and the maintenance continues with every Chrome update and every memory leak that surfaces under load. For teams where screenshots are the core product, that investment is justified. For OG images as a side feature, it rarely is.
How an OG image generator API works
Design an HTML template sized at 1200x630 pixels (the standard Open Graph dimensions), pass it to the screenshotrun API, and get a PNG back. The API opens your HTML in a real Chromium browser, renders the full CSS and JavaScript, captures the viewport, and returns the image. Any CSS property that works in Chrome works in the screenshot.
Two approaches work depending on your setup.
If you host the template on your server (an Express route, a Laravel Blade view, a Django template), you pass the URL to the API with query parameters for the dynamic data. The API opens that URL and captures it — a straightforward way to generate an OG image from a URL.
If you don't want to host anything, you pass raw HTML directly through the html parameter. The API renders the markup without needing a live URL. This is useful for serverless environments, quick prototypes, or when you build OG images from HTML templates dynamically in application code.
Generate an OG image with one API call
This cURL request turns an HTML template into an OG image.
curl "https://api.screenshotrun.com/v1/screenshots/capture?url=https://yoursite.com/og-template?title=My+Article&width=1200&height=630&format=png&response_type=image" \
-H "Authorization: Bearer YOUR_API_KEY" \
--output og-image.png
It sets the viewport to exactly 1200x630, renders the page, and returns a PNG. The response_type=image parameter tells the API to return the raw image binary instead of a JSON response — without it you'd get JSON metadata by default. The output is ready to drop into your og:image meta tag.
For templates that load Google Fonts or external images, add a delay parameter (1500-2000ms is enough in most cases) so the browser finishes loading resources before capturing.
Node.js with inline HTML template
const axios = require('axios');
const fs = require('fs');
async function generateOgImage(title, category) {
const html = `
<html>
<head>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@600;800&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { width: 1200px; height: 630px; overflow: hidden;
font-family: 'Inter', sans-serif;
background: linear-gradient(135deg, #0f172a, #1e293b);
color: #fff; display: flex; flex-direction: column;
justify-content: center; padding: 60px; }
.tag { font-size: 16px; text-transform: uppercase;
letter-spacing: 2px; color: #38bdf8; margin-bottom: 20px; }
h1 { font-size: 52px; font-weight: 800; line-height: 1.15;
max-width: 900px; }
</style>
</head>
<body>
<div class="tag">${category}</div>
<h1>${title}</h1>
</body>
</html>`;
const response = await axios.get(
'https://api.screenshotrun.com/v1/screenshots/capture',
{
params: {
html: html,
width: 1200,
height: 630,
format: 'png',
delay: 1500,
response_type: 'image'
},
headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
responseType: 'arraybuffer'
}
);
return Buffer.from(response.data);
}
const image = await generateOgImage('Build an API in 10 Minutes', 'Tutorial');
fs.writeFileSync('og-image.png', image);
Pass your markup through the html parameter and skip hosting entirely — no endpoint to deploy, no URL to expose.
Python
import requests
def generate_og_image(title, category):
params = {
'url': f'https://yoursite.com/og-template?title={title}&category={category}',
'width': 1200,
'height': 630,
'format': 'png',
'delay': 1500,
'response_type': 'image'
}
response = requests.get(
'https://api.screenshotrun.com/v1/screenshots/capture',
params=params,
headers={'Authorization': 'Bearer YOUR_API_KEY'}
)
response.raise_for_status()
return response.content
image = generate_og_image('Build an API in 10 Minutes', 'Tutorial')
with open('og-image.png', 'wb') as f:
f.write(image)
API parameters that affect OG image quality
Beyond width, height, and format, a few parameters affect the final image quality and file size.
| Parameter | Value | What it does for OG images |
|---|---|---|
width + height |
1200 x 630 | Standard OG dimensions. Works on Facebook, Twitter/X, LinkedIn, Slack, Discord, Telegram |
format |
png / webp / jpeg | PNG for crisp text. WebP for 40-50% smaller files. JPEG for photo-heavy backgrounds. See format options |
retina |
true | 2x resolution (2400x1260). Text looks noticeably sharper on high-DPI screens |
delay |
1500-2000 | Wait for web fonts and images to load before capturing |
quality |
80-85 | For JPEG/WebP. Cuts file size in half with minimal visual difference |
cache_ttl |
86400 | Cache the result for 24 hours. Repeat requests return the cached image instantly |
dark_mode |
true | Forces prefers-color-scheme: dark. Useful if your template adapts to dark mode |
omit_background |
true | Transparent PNG. Useful for OG overlays or compositing |
If your publishing system regenerates OG images on a schedule, cache_ttl avoids burning API credits on repeat captures of the same template. See the full parameter reference for details.
OG image generator API vs Satori, Puppeteer, and manual design
Four approaches exist for generating dynamic OG images at scale. Each trades off something.
| Screenshot API | Satori / Vercel OG | Self-hosted Puppeteer | Manual (Figma/Canva) | |
|---|---|---|---|---|
| CSS support | Full (any browser CSS) | Flexbox subset only | Full | N/A |
| Setup time | Minutes (one API call) | Hours (learn JSX API) | Days (infra + queue) | Minutes per image |
| Infrastructure | None | Vercel Edge (vendor lock-in) | Your server + Chrome | None |
| Scales to 500+ pages | Yes | Yes | Yes (with effort) | No |
| Custom fonts | Google Fonts via link tag | Manual .woff loading | System or installed fonts | Any font |
| Framework dependency | None (any language) | Next.js / Vercel | Node.js | None |
| Cost | Per screenshot, free tier available | Free (Vercel limits) | Server cost + dev time | Designer time |
Satori is a good fit if your templates are simple (text on a solid background) and you already deploy on Vercel. The moment you need Grid layout, box shadows, or custom styling that goes beyond Flexbox, it stops working. A screenshot API doesn't have that limitation because it renders in a real browser.
Self-hosted Puppeteer makes sense for teams generating thousands of images daily where the per-screenshot API cost would exceed server costs. For most blogs and SaaS products generating a few hundred OG images total, the API approach costs less and requires zero maintenance.
Where OG images matter beyond blog posts
Blog posts are the obvious use case, but OG images affect any page that gets shared. SaaS product pages, documentation, user profiles, event pages, changelogs — all of these end up in Slack channels and Discord servers. A proper preview card with the page title, a relevant visual, and your brand makes the link recognizable. This overlaps with link preview generation, which covers the broader pattern.
For SaaS products with user-generated content (portfolios, dashboards, public profiles), a social media preview image API scales to thousands of cards. A single branded layout handles every user page — swap the name, avatar, and stats at render time, and the HTML-to-image API returns a unique card for each profile.
E-commerce stores benefit from product-specific OG cards that include the product name, price, and a thumbnail. When someone shares a product link in a group chat, a branded card with the actual product image converts better than a generic store logo.
Plugging OG images into your publishing workflow
Regardless of framework, the integration follows the same pattern. When an article is published or updated, a background job calls the API with the article's metadata, saves the returned image, and writes the URL to the database. Your page template then references that URL in the og:image meta tag.
In Laravel, this is a model Observer that dispatches a queued Job. In Django, a post_save signal. In Express, a post-publish webhook. The API call itself is identical everywhere: send a URL (or HTML), get a PNG, save it.
For sites that already have published content without OG images, a batch script loops through existing pages and generates one image per page. Add a 300ms pause between requests and the entire backlog of 200 articles finishes in about a minute.
Once generated, store the images wherever your static assets live. A public/og-images/ directory on your server works for sites with up to a few hundred pages. For larger volumes, push to S3 or Hetzner Object Storage with a CDN in front. File sizes stay small: a 1200x630 PNG with text on a gradient background runs 200-400 KB. WebP cuts that roughly in half.
Open Graph meta tags for social media previews
Your API call produces the image. Your page needs the right tags for social platforms to find it.
<!-- Open Graph (Facebook, LinkedIn, Telegram, Slack) -->
<meta property="og:image" content="https://yoursite.com/og-images/my-article.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<!-- Twitter/X -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="https://yoursite.com/og-images/my-article.png" />
Include the og:image:width and og:image:height tags. Without them, Facebook makes an extra request to figure out the dimensions and sometimes shows a small preview instead of a large card. A few extra bytes of HTML prevent that.
For a step-by-step walkthrough of building HTML templates and integrating OG generation into your code, the OG image generation tutorial covers templates, storage options, and debugging in detail. The custom viewport feature page explains how width and height parameters control the capture dimensions across different use cases.
The free tier covers OG image generation for most blogs and mid-sized SaaS products. Create a free API key and test it on your next page. Paid plans for higher volumes start at $9/month — see pricing for details.
Frequently asked questions
What size should OG images be?
The standard is 1200x630 pixels with a 1.91:1 aspect ratio. This works across Facebook, Twitter/X, LinkedIn, Slack, Discord, and Telegram. Set width=1200 and height=630 in your API request to match this exactly. Keep file size under 1 MB for reliability across all platforms.
How do I generate OG images without Vercel or Next.js?
Use an OG image generator API with any stack. Design your OG template as plain HTML and CSS, then send it to the API via the html parameter or as a hosted URL. The API renders it in Chromium and returns a PNG. No framework dependency, no edge function, no JSX.
Can I use custom fonts in my OG images?
Yes. Add a Google Fonts <link> tag in your HTML template's head section. The API loads the font before capturing. Set a delay of 1500-2000ms to make sure the font fully loads. System fonts (Arial, Segoe UI, Helvetica) work without any delay.
Do I need to host my HTML template somewhere?
No. The screenshotrun API accepts raw HTML through the html parameter. You can send your entire template markup in the request body without deploying it to a URL. This is useful for serverless environments or when you generate templates dynamically in code.
How do I automate OG images for hundreds of existing pages?
Write a script that loops through your published pages, calls the API once per page with the page's metadata injected into the template, and saves the returned image. At 300ms between requests, 200 pages take about a minute. Store images by slug (article-title.png) and reference them in your meta tags.