Skip to content

OG image API — generate 1200×630 Open Graph images

Generates 1200×630 PNG Open Graph images from an HTML template. Templates are plain HTML and CSS rendered in real Chromium, so CSS grid and web fonts work.

There are two ways to get an image:

Use Endpoint Auth
Server-side (build step, backend, cron) POST /v1/og API key
Public link in <meta property="og:image"> GET /v1/og?…&sig=… Signed URL
POST https://api.shotvik.com/v1/og
Content-Type: application/json
Authorization: Bearer <api_key>

Send exactly one of template or html. Unknown fields are rejected.

Field Type Default Description
template string A built-in template (basic, article) or the ID of one of your stored templates (tpl_…).
html string Inline template HTML with {{var}} placeholders, max 1,048,576 bytes.
vars object of strings Template variables: at most 30, each value up to 1,000 characters. Names use A–Z, a–z, 0–9 and _ (max 40 characters).
cache boolean false Opt-in cache on our Hetzner servers. Cache hits don’t count toward your quota.
cache_ttl integer, 60–2592000 86400 Cache lifetime in seconds (max 30 days, provisional). Only with cache: true.

The output size is always 1200×630 and the format is always PNG. Width, height and format can’t be set here.

Terminal window
curl https://api.shotvik.com/v1/og \
-H "Authorization: Bearer $SHOTVIK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"template": "basic", "vars": {"title": "Hello", "subtitle": "My first OG image", "site": "example.com"}}' \
--fail-with-body --output og.png

Response: 200 with Content-Type: image/png and the headers listed under Screenshot a URL, except X-Target-Status. Errors: 400, 401, 403, 404 (template_not_found), 413, 429, 500, 503, 504. See Errors.

GET https://api.shotvik.com/v1/og?kid=<key_id>&template=<template>&vars[title]=Hello&sig=<signature>
Query parameter Required Description
kid yes Your public key ID (the 12-character ID that is part of your API key). The API key itself is never in the URL.
template yes A built-in template name or a stored template ID. Inline HTML isn’t possible in a GET URL.
vars[<name>] no One parameter per template variable, e.g. vars[title]=Hello. Same limits as above.
exp no Expiry as a Unix timestamp (seconds). After it, the link returns 403 signature_expired. Without exp, the link doesn’t expire.
cache no true or false.
cache_ttl no Cache lifetime in seconds, as above.
sig yes The signature. See Signed URLs.

Every parameter except sig is covered by the signature, so changing anything (including cache or exp) invalidates the link. A parameter that appears twice is rejected. Revoking or blocking the key, or rotating its signing secret, invalidates all of its signed URLs.

Response: 200 with Content-Type: image/png. Errors:

  • 400;
  • 401 (invalid_signature);
  • 403 (signature_expired, or the key is suspended);
  • 404;
  • 429, 500, 503, 504.

Crawlers (Slack, LinkedIn, X, WhatsApp and others) fetch OG images repeatedly. Without caching, every fetch is a new render and counts toward your quota.

With cache=true:

  • the image is stored on Shotvik’s Hetzner servers in Germany/Finland for cache_ttl seconds (max 30 days, provisional), and served from there; there is no CDN in front of the API;
  • on signed GET links, the response carries Cache-Control: public, max-age=<cache_ttl>, so browsers and crawlers may keep their own copies, even after you revoke the key;
  • cache hits return X-Cache: HIT and don’t count toward your quota. They carry the same X-Shotvik-Warnings as the original render, if any.

Without caching, responses are sent with Cache-Control: private, no-store.

Syntax: {{name}} inserts a variable, and {{name|fallback}} uses fallback when the variable isn’t set. Values are always HTML-escaped. There is no logic (no loops or conditions) and no way to output raw HTML. For custom fonts, use @font-face in your template HTML; fonts are loaded through the same safety checks as other resources. If a font or image can’t be loaded, the image is still rendered and the response lists it in X-Shotvik-Warnings (for example font:blocked:1).

Built-in templates:

Name Description Variables
basic Title and subtitle on a gradient, site name in the footer. title, subtitle, site, accent
article Blog/article card: category tag, title, author and date, CSS grid layout. category, title, author, date, site

Stored templates (API key required):

Method and path Body Response
POST /v1/templates {"name": "...", "html": "..."}: name 1–100 characters, HTML max 1,048,576 bytes 201 {"id": "tpl_…", "name": "..."}
GET /v1/templates 200 {"builtin": [{name, description, vars}], "templates": [{id, name, created_at}]}
DELETE /v1/templates/{id} 204, or 404 template_not_found

Stored templates are kept until you delete them or your account.

See Dynamic OG images in Next.js and Astro.

Shotvik is in private beta. Request access: support@shotvik.com · Privacy