Beautiful OG images. One API call.
Generate stunning Open Graph images for your website with a single URL — no design tools, no headless browsers, no infrastructure to manage.
Every page you share on Twitter, Slack, or Discord needs an OG image. You can:
Design each one manually in Figma(doesn't scale)Run a headless browser on your server(slow, expensive, breaks)- Call ShotOG's API — one URL, beautiful image, ~50ms at the edge
ShotOG runs on Cloudflare Workers. No cold starts. No servers. Just fast images.
Drop this into your HTML <head>:
<meta property="og:image" content="https://shotog.2214962083.workers.dev/v1/og?title=My%20Page%20Title&template=blog&author=John" />That's it. No signup required for up to 10 images/day.
npm install shotogimport { ShotOG } from "shotog";
const og = new ShotOG({ apiKey: "sk_..." }); // optional, increases rate limit
// Generate a URL (no network request)
const imageUrl = og.url({
title: "How We Scaled to 1M Users",
subtitle: "A deep dive into our infrastructure",
template: "blog",
author: "Jane Smith",
});
// → https://shotog.2214962083.workers.dev/v1/og?title=How+We+Scaled...
// Or fetch the image binary directly
const imageBuffer = await og.generate({
title: "Product Launch",
template: "announcement",
});# GET — returns PNG directly
curl "https://shotog.2214962083.workers.dev/v1/og?title=Hello&template=basic" -o og.png
# POST — JSON body, more options
curl -X POST https://shotog.2214962083.workers.dev/v1/og \
-H "Content-Type: application/json" \
-d '{"title":"Hello World","template":"product","subtitle":"Built with ShotOG"}' \
-o og.png8 professionally designed templates for every use case:
GET /v1/og?title=...&template=...
POST /v1/og (JSON body)
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
title |
string | Yes | Main heading text |
template |
string | No | Template name (default: basic) |
subtitle |
string | No | Secondary text |
eyebrow |
string | No | Small text above title (category, label) |
author |
string | No | Author name |
avatar |
string | No | Avatar image URL (shown in blog/social/testimonial) |
logo |
string | No | Logo image URL (shown in basic/product) |
fontUrl |
string | No | URL to TTF/OTF font file (max 5MB, cached 1h) |
domain |
string | No | Domain watermark |
bgColor |
string | No | Background color (hex, e.g. 1a1a2e) |
textColor |
string | No | Text color (hex) |
accentColor |
string | No | Accent color (hex) |
format |
string | No | png (default) or svg |
width |
number | No | Image width 200-2400 (default: 1200) |
height |
number | No | Image height 200-1260 (default: 630) |
api_key |
string | No | API key for higher limits |
curl -X POST https://shotog.2214962083.workers.dev/v1/og/batch \
-H "Content-Type: application/json" \
-H "X-Api-Key: sk_..." \
-d '{
"images": [
{"id": "hero", "title": "My Product", "template": "product"},
{"id": "blog-1", "title": "First Post", "template": "blog", "author": "Alice"},
{"id": "blog-2", "title": "Second Post", "template": "blog", "author": "Bob"}
],
"defaults": {"format": "png", "width": 1200, "domain": "example.com"}
}'Response:
{
"results": [
{"id": "hero", "success": true, "dataUri": "data:image/png;base64,..."},
{"id": "blog-1", "success": true, "dataUri": "data:image/png;base64,..."},
{"id": "blog-2", "success": true, "dataUri": "data:image/png;base64,..."}
],
"summary": {"total": 3, "succeeded": 3, "failed": 0}
}Batch features:
- Max 20 images per request
defaultsobject applies to all images (individual params override)- Parallel rendering via
Promise.allSettled - Quota pre-check: if insufficient quota, returns 429 before rendering
- Only successful renders count toward usage
curl -X POST https://shotog.2214962083.workers.dev/v1/keys \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com"}'Returns:
{
"id": "key_abc123",
"key": "sk_live_...",
"tier": "free",
"monthly_limit": 500,
"message": "Store your API key safely — it cannot be retrieved later."
}curl https://shotog.2214962083.workers.dev/v1/keys/usage \
-H "X-Api-Key: sk_live_..."curl https://shotog.2214962083.workers.dev/v1/og/templatesThe TypeScript/JavaScript SDK wraps all API functionality:
import { ShotOG } from "shotog";
// Initialize
const og = new ShotOG({ apiKey: "sk_..." });
// Build image URL (no network call)
og.url({ title: "My Post", template: "blog" });
// Generate image binary
await og.generate({ title: "My Post", template: "blog" });
// List templates
await og.templates();
// Check usage
await og.usage();
// Create a new API key (static method)
await ShotOG.createKey("email@example.com");// app/layout.tsx
export function generateMetadata({ params }) {
return {
openGraph: {
images: [`https://shotog.2214962083.workers.dev/v1/og?title=${encodeURIComponent(params.title)}&template=blog`],
},
};
}---
const ogImage = `https://shotog.2214962083.workers.dev/v1/og?title=${encodeURIComponent(title)}&template=product`;
---
<meta property="og:image" content={ogImage} />{{ $ogImage := printf "https://shotog.2214962083.workers.dev/v1/og?title=%s&template=blog&author=%s" (querify .Title) (querify .Params.author) }}
<meta property="og:image" content="{{ $ogImage }}" />- Runtime: Cloudflare Workers (edge, ~17ms cold start)
- Framework: Hono (lightweight, fast)
- Rendering: @cf-wasm/satori (JSX → SVG at the edge)
- Rasterizer: @cf-wasm/resvg (SVG → PNG)
- Database: Cloudflare D1 (API keys + usage tracking)
- Caching: Built-in CDN cache headers
ShotOG is MIT licensed. Deploy your own instance:
git clone https://github.com/nicepkg/shotog.git
cd shotog
npm install
# Set up D1 database
npx wrangler d1 create shotog-prod
# Update wrangler.toml with your database_id
npx wrangler d1 execute shotog-prod --file=./migrations/001_init.sql
# Deploy
npx wrangler deploy| Plan | Price | Monthly Images | Rate Limit |
|---|---|---|---|
| Demo | Free | 10/day | 10/day |
| Free | Free | 500/month | 60/min |
| Starter | $9/mo | 5,000/month | 120/min |
| Pro | $29/mo | 25,000/month | 300/min |
| Scale | $79/mo | 100,000/month | 600/min |