Links shared on X, LinkedIn, Slack or Discord get far more clicks with a proper preview card. Static sites usually ship one generic og:image for every page, because generating images means a server function (@vercel/og, Satori) or a slow build step with headless Chrome. A signed image URL avoids both. Your build writes a URL into the meta tag, and the card is rendered the first time a crawler fetches it.
GET /v1/account returns its public key_id.https://shotmint.creasework.com/og/KEY_ID.png?title=…&subtitle=…&site=…&sig=…. The signature locks the text, so nobody can render other cards on your credits.og:image and twitter:image. The first fetch renders a 1200×630 PNG in real Chromium (1 credit). Repeats within 24h are served from cache for free, with Cache-Control: public, max-age=86400.<meta property="og:image" content="https://shotmint.creasework.com/og/KEY_ID.png?title=Why%20we%20moved%20to%20Astro&site=blog.example.com&sig=...">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://shotmint.creasework.com/og/KEY_ID.png?title=Why%20we%20moved%20to%20Astro&site=blog.example.com&sig=...">
Want to see the cards first? The OG image generator previews all five themes (indigo, dark, light, sunset, mint) and makes signed tags one page at a time.
The signature is a plain HMAC: HMAC-SHA256 with your API key over title, subtitle, site and theme joined by newlines (empty string for unset fields, theme defaults to indigo), hex, first 32 characters. Keep the key in a build-time environment variable. Only the key_id ends up in your HTML.
import crypto from "node:crypto";
export function ogImage({ title = "", subtitle = "", site = "", theme = "indigo" }) {
const sig = crypto.createHmac("sha256", process.env.SHOTMINT_KEY)
.update([title, subtitle, site, theme].join("\n")).digest("hex").slice(0, 32);
const q = new URLSearchParams();
if (title) q.set("title", title);
if (subtitle) q.set("subtitle", subtitle);
if (site) q.set("site", site);
if (theme !== "indigo") q.set("theme", theme);
q.set("sig", sig);
return "https://shotmint.creasework.com/og/" + process.env.SHOTMINT_KEY_ID + ".png?" + q;
}
ogImage({ title: "Why we moved to Astro", subtitle: "Faster builds, less JS", site: "blog.example.com" });
Next.js App Router: return it from generateMetadata: openGraph: { images: [{ url: ogImage({ title: post.title, site: "example.com" }), width: 1200, height: 630 }] }. Because the image is hosted elsewhere, you don't need an opengraph-image.tsx route or the edge runtime, and it works with output: "export".
Astro: call it in your layout's frontmatter (const og = ogImage({ title: Astro.props.title, site: Astro.site.host })) and write <meta property="og:image" content={og} />. Use a non-PUBLIC_ env var so the key stays out of client bundles.
import hashlib, hmac, os
from urllib.parse import urlencode, quote
def og_image(title="", subtitle="", site="", theme="indigo"):
msg = "\n".join([title, subtitle, site, theme])
sig = hmac.new(os.environ["SHOTMINT_KEY"].encode(), msg.encode(), hashlib.sha256).hexdigest()[:32]
q = {k: v for k, v in [("title", title), ("subtitle", subtitle), ("site", site)] if v}
if theme != "indigo":
q["theme"] = theme
q["sig"] = sig
return "https://shotmint.creasework.com/og/" + os.environ["SHOTMINT_KEY_ID"] + ".png?" + urlencode(q, quote_via=quote)
og_image(title="Why we moved to Astro", site="blog.example.com")
Hugo or Jekyll? Generate a small JSON map of page path → og:image URL with the Python or Node function in a pre-build step, then read it in your template (site.Data in Hugo, site.data in Jekyll).
/v1/screenshot with width=1200&height=630 at build time and host the PNG yourself (see the HTML to image tool).One new card = one credit, so a 300-post blog costs 300 credits once. Each card is rendered again at most once a day, and only when a crawler actually asks for it. €5 buys 2,000 credits, credits never expire, and there's no subscription. A failed render is refunded automatically.
No signup, no card. Get a key in one click, then buy credits only if you need more (from €5 for 2,000, never expire).
Get a free API key Full API reference