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.
Why ShotOG?
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.
Quick Start
Option 1: Just use a URL
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.
Option 2: Use the TypeScript SDK
import { 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", });
Option 3: cURL
# 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.png
Templates
8 professionally designed templates for every use case:
API Reference
Generate OG Image
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 |
Batch Generate (up to 20 images)
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
Get API Key (Self-Service)
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."
}Check Usage
curl https://shotog.2214962083.workers.dev/v1/keys/usage \
-H "X-Api-Key: sk_live_..."List Templates
curl https://shotog.2214962083.workers.dev/v1/og/templates
SDK
The 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");
Framework Examples
Next.js
// app/layout.tsx export function generateMetadata({ params }) { return { openGraph: { images: [`https://shotog.2214962083.workers.dev/v1/og?title=${encodeURIComponent(params.title)}&template=blog`], }, }; }
Astro
--- const ogImage = `https://shotog.2214962083.workers.dev/v1/og?title=${encodeURIComponent(title)}&template=product`; --- <meta property="og:image" content={ogImage} />
Hugo
{{ $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 }}" />Tech Stack
- 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
Self-Hosting
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
Pricing
| 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 |