API reference
One endpoint. GET it, get a 1200×630 PNG back.
Endpoint
GET /api/og
Returns image/png on success. Errors return JSON with an error field: 400 invalid parameters, 401 bad key, 429 quota or rate limit exceeded.
Parameters
| Param | Type | Default | Description |
|---|---|---|---|
| key | string | — | Your API key (og_…). Omit for watermarked, rate-limited demo renders. |
| template | gradient | minimal | split | terminal | quote | announce | gradient | Which card layout to render. |
| title | string (≤ 200 chars) | Hello, world | Main headline. Font size auto-scales with length. |
| subtitle | string (≤ 300 chars) | — | Supporting line under the title. |
| site | string (≤ 100 chars) | — | Site or brand name shown in the footer of the card. |
| theme | dark | light | dark | Base color scheme (the terminal template is always dark). |
| accent | hex color | #6366f1 | Accent color. URL-encode the hash: %236366f1. |
| acct + sig | string | — | Signed-URL auth: your account ID plus an HMAC signature. Alternative to key — see Signed URLs below. |
Use it in your HTML
<meta property="og:image" content="https://ogsmith.app/api/og?key=og_yourkey&template=split&title=My%20post&site=example.com&accent=%23f43f5e" /> <meta name="twitter:card" content="summary_large_image" />
Next.js example
export function generateMetadata({ params }) {
const og = new URL("https://ogsmith.app/api/og");
og.searchParams.set("key", process.env.OGSMITH_KEY);
og.searchParams.set("title", post.title);
og.searchParams.set("site", "example.com");
return { openGraph: { images: [og.toString()] } };
}Account defaults
Set default template, theme, accent, and site values in the dashboard (Brand defaults). They apply to authenticated renders whenever the parameter is omitted, so a URL can be as short as /api/og?key=…&title=Hello. Parameters in the URL always override defaults. You can create up to 10 named API keys and revoke them independently — per-key render counts show on the dashboard. On paid plans, an uploaded logo (dashboard → Brand defaults) renders on every card next to the site name.
Signed URLs
Instead of embedding your API key, you can sign each URL with your account's signing secret (dashboard → Signed URLs). The signature binds the exact parameters, so a leaked URL can't be modified or reused for other content, and your secret never appears in markup.
Signature: take every query parameter except sig (including acct), percent-encode each name and value with encodeURIComponent, sort pairs by name, join as name=value with &, and HMAC-SHA256 it with your secret (hex output).
import { createHmac } from "crypto";
function signedOgUrl(params, accountId, secret) {
const p = new URLSearchParams(params);
p.set("acct", accountId);
const msg = [...p.entries()]
.map(([k, v]) => [encodeURIComponent(k), encodeURIComponent(v)])
.sort(([a, x], [b, y]) =>
a === b ? x.localeCompare(y) : a.localeCompare(b))
.map(([k, v]) => `${k}=${v}`)
.join("&");
p.set("sig", createHmac("sha256", secret).update(msg).digest("hex"));
return `https://ogsmith.app/api/og?${p}`;
}
signedOgUrl({ title: "My post", template: "split" }, ACCOUNT_ID, SECRET);Check your usage
Poll your quota programmatically — useful for alerting before you hit the cap:
GET /api/usage
Authorization: Bearer og_yourkey
{ "month": "2026-08", "plan": "pro", "used": 1204,
"limit": 20000, "remaining": 18796, "watermark": false }Quotas & caching
- Each successful render counts one unit against your monthly quota.
- Responses are CDN-cached for 24 hours — repeat crawls of the same URL usually never hit the API (and never count against quota).
- Quotas reset on the 1st of each month (UTC).
- You get one email at 80% of quota and one when the cap is reached — never more than one of each per month.
- Demo requests (no key) are watermarked and limited to ~20/minute.
Keep your key secret-ish
OG image URLs are public by nature — crawlers must fetch them — so the key rides in the URL. That's standard for this category of API. If a key leaks and gets abused, rotate it from the dashboard; old URLs stop working immediately and your quota protects your bill (rendering stops at the cap — there are no overage charges).