OGPipe API Documentation
Generate beautiful Open Graph images from templates or custom HTML with a single API call. Images are rendered with Chromium, cached on a global CDN, and served in milliseconds.
Base URL: https://api.ogpipe.dev
Authentication
All requests require an API key passed in the Authorization header.
Authorization: Bearer og_live_your_api_key_here
API keys start with og_live_ followed by 32 hex characters. Get your key at ogpipe.dev.
Quick Start
Generate your first OG image in 30 seconds:
cURL
curl -X POST https://api.ogpipe.dev/images \
-H "Authorization: Bearer og_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"template": "blog",
"data": {
"title": "How We Scaled to 1M Users",
"author": "Jane Smith",
"date": "June 2026",
"site": "acme.dev"
}
}'
Node.js
const response = await fetch("https://api.ogpipe.dev/images", {
method: "POST",
headers: {
"Authorization": "Bearer og_live_your_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
template: "blog",
data: {
title: "How We Scaled to 1M Users",
author: "Jane Smith",
date: "June 2026",
site: "acme.dev",
},
}),
});
const { url } = await response.json();
// → https://d1234.cloudfront.net/renders/a1b2c3.png
Python
import requests
resp = requests.post(
"https://api.ogpipe.dev/images",
headers={"Authorization": "Bearer og_live_your_key"},
json={
"template": "blog",
"data": {
"title": "How We Scaled to 1M Users",
"author": "Jane Smith",
"date": "June 2026",
"site": "acme.dev",
},
},
)
url = resp.json()["url"]
Next.js Plugin: Installation
The @ogpipe/next package adds automatic OG image generation to your Next.js project with full CSS support.
npm install @ogpipe/next
Set your API key as an environment variable:
# .env.local
OGPIPE_API_KEY=og_live_your_key_here
Add the generate step to your build script:
// package.json
{
"scripts": {
"build": "next build && ogpipe generate"
}
}
Configuration
Create ogpipe.config.ts in your project root:
import { defineConfig } from '@ogpipe/next'
export default defineConfig({
templates: {
blog: { file: './og-templates/blog.html' },
docs: { file: './og-templates/docs.html' },
default: {
html: `<div style="display:flex; width:1200px; height:630px;
background:#1a1a2e; align-items:center; padding:60px;">
<h1 style="color:white; font-size:56px;">{{title}}</h1>
</div>`,
},
},
routes: {
'/blog/[slug]': {
template: 'blog',
vars: (meta) => ({
title: meta.title || 'Untitled',
author: meta.params?.slug || '',
}),
},
'/docs/[slug]': {
template: 'docs',
vars: (meta) => ({
title: meta.title || '',
category: 'Documentation',
description: meta.description || '',
}),
},
'*': { template: 'default' },
},
outDir: 'public/og', // default
})
Config Options
| Field | Type | Description |
|---|---|---|
apiKey | string | API key. Defaults to OGPIPE_API_KEY env var. |
baseUrl | string | API base URL. Defaults to https://api.ogpipe.dev |
templates | object | Named templates (inline HTML or file path) |
routes | object | Route patterns → template mapping |
onDemand | object | On-demand rendering config (revalidate, fallback) |
outDir | string | Output directory for generated images (default: public/og) |
Template Options
| Field | Type | Description |
|---|---|---|
html | string | Inline HTML with {{variable}} placeholders |
file | string | Path to an HTML template file (relative to config) |
width | number | Image width (default: 1200) |
height | number | Image height (default: 630) |
format | string | Output format: png, jpeg, webp (default: png) |
Build-Time Generation
When you run next build && ogpipe generate, the CLI:
- Reads
.next/prerender-manifest.jsonto discover all static routes - Matches each route against your
ogpipe.config.tspatterns - Renders OG images via the API (5 concurrent requests)
- Writes PNGs to
public/og/ - Outputs
og-manifest.jsonlisting all generated images
$ npx ogpipe generate
⚡ OGPipe — Generating OG images...
✅ Generated 12 OG images in 4200ms
/blog/hello-world → /og/blog/hello-world.png (380ms)
/blog/scaling-apis → /og/blog/scaling-apis.png (412ms)
/docs/getting-started → /og/docs/getting-started.png (395ms)
...
📁 Output: public/og/
📋 Manifest: public/og/og-manifest.json
Key benefit: Generated images are static files. No runtime API dependency. They survive outages, work offline, and are served instantly by any CDN.
Dynamic Routes (On-Demand)
For pages that don't exist at build time (new blog posts, user content), use the on-demand handler:
// app/blog/[slug]/opengraph-image.ts
import { OGImageHandler } from '@ogpipe/next'
export default OGImageHandler({
html: `<!DOCTYPE html>
<html><head>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="w-[1200px] h-[630px] flex flex-col justify-center p-16 bg-slate-900">
<h1 class="text-5xl font-bold text-white">{{title}}</h1>
</body></html>`,
vars: (params) => ({
title: params.slug.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase()),
}),
revalidate: 86400, // CDN cache for 24 hours
fallback: '/og-fallback.png', // shown if API is unavailable
})
First request: renders via API (~1-2s). All subsequent requests: served from CDN cache.
Dev Preview Server
See your OG images in real-time with hot-reload:
$ npx ogpipe dev
⚡ OGPipe Dev Preview
→ http://localhost:3010
3 routes configured
Watching templates for changes...
The preview server shows your OG images rendered inside platform frames (Twitter, LinkedIn, Slack, Discord). Edit a template file → preview updates instantly via hot-reload.
Migrating from @vercel/og
If you're switching from @vercel/og (Satori), here's what changes:
| Concern | @vercel/og | @ogpipe/next |
|---|---|---|
| CSS support | Flexbox subset only | Full browser CSS |
| Font loading | Manual ArrayBuffer | <link> tag in HTML |
| Template format | JSX + inline styles | HTML/CSS (Tailwind works) |
| Rendering | Always at request time | Build-time (default) + on-demand |
| Hosting | Vercel Edge only | Any platform |
| CSS Grid | ❌ | ✅ |
| WOFF2 fonts | ❌ | ✅ |
calc(), z-index | ❌ | ✅ |
Full migration guide: See Migrating from @vercel/og for step-by-step before/after examples.
Generate Image
POST/images
Render an OG image from a template or custom HTML. Returns a permanent CDN URL for the generated image.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
template | string | * | Built-in template ID: blog, docs, minimal |
html | string | * | Raw HTML to render (alternative to template) |
data | object | No | Key-value pairs to inject into template placeholders |
width | number | No | Image width in pixels (default: 1200) |
height | number | No | Image height in pixels (default: 630) |
format | string | No | Output format: png, jpeg, webp (default: png) |
* Provide either template (with optional data) or html. Not both.
Response
{
"url": "https://d1234.cloudfront.net/renders/a1b2c3d4.png",
"width": 1200,
"height": 630,
"format": "png",
"cached": false
}
{
"error": "Provide either \"template\" (with \"data\") or raw \"html\"."
}
{
"error": "Missing API key. Include header: Authorization: Bearer og_live_xxxxx"
}
{
"error": "Monthly limit reached (50 images). Upgrade your plan at ogpipe.dev/pricing"
}
List Templates
GET/templates
Returns all available built-in templates. No authentication required.
{
"templates": [
{ "id": "blog", "description": "Clean blog post card — title, author, date, site name" },
{ "id": "docs", "description": "Documentation page — category badge, title, description, brand" },
{ "id": "minimal", "description": "Just a big title on a colored background" }
]
}
Template: Blog
A clean blog post social card with title, author, date, and site name on a dark gradient background.
Variables
| Variable | Description | Example |
|---|---|---|
title | Post title (main heading) | How We Scaled to 1M Users |
author | Author name | Jane Smith |
date | Publication date | June 2026 |
site | Site name / domain | acme.dev |
Template: Docs
A documentation-style card with category badge, title, description, and brand. Light background with mono font accents.
Variables
| Variable | Description | Example |
|---|---|---|
category | Category badge text | API Reference |
title | Page title | Authentication Guide |
description | Short description | Learn how to authenticate API requests |
site | Brand name | Acme Docs |
Template: Minimal
A big centered title on a solid colored background. Great for announcements or simple social cards.
Variables
| Variable | Description | Example |
|---|---|---|
title | The text to display | We just launched! |
background | CSS background value | #1e40af |
color | Text color | #ffffff |
Custom HTML
Skip templates entirely and provide your own HTML. The entire page is rendered at the specified viewport size and screenshotted.
curl -X POST https://api.ogpipe.dev/images \
-H "Authorization: Bearer og_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"html": "<!DOCTYPE html><html><body style=\"width:1200px;height:630px;display:flex;align-items:center;justify-content:center;background:#000;color:#fff;font-size:48px;font-family:sans-serif\">Hello OGPipe</body></html>",
"width": 1200,
"height": 630
}'
Tip: Your HTML should set explicit width and height on the body to match your requested dimensions. Web fonts from Google Fonts and other CDNs are supported.
Rate Limits
Usage is tracked monthly per API key. Limits reset on the 1st of each month (UTC).
| Plan | Monthly Renders | Price |
|---|---|---|
| Free | 50 | $0 |
| Pro | 5,000 | $19/mo |
| Scale | 25,000 | $49/mo |
| Enterprise | Unlimited | Contact us |
When you hit your limit, the API returns 429 with a message indicating how to upgrade.
Caching
Images are cached deterministically. The same input (HTML + dimensions + format) always produces the same cache key. This means:
- Repeated requests with identical input return the same URL instantly
- Images are stored for 30 days on S3 with a CloudFront CDN in front
- CloudFront serves cached images globally with sub-50ms latency
Note: The cached field in the response indicates whether the image was rendered fresh (false) or served from cache (true). Both return the same URL.
Error Handling
All errors return a JSON body with an error field:
| Status | Meaning | Resolution |
|---|---|---|
| 400 | Bad request — invalid input | Check request body format and required fields |
| 401 | Missing or invalid API key | Include a valid Authorization: Bearer og_live_xxx header |
| 429 | Monthly limit exceeded | Upgrade your plan or wait for monthly reset |
| 500 | Render failed | Check your HTML for errors. If persistent, contact support. |