API reference

API documentation.

Base URL https://api.shotium.com/v1 · errors follow RFC 9457 · all endpoints return image bytes on success.

Quick start

  1. Sign in with GitHub or Google — your account starts with 100 free render credits.
  2. Create an API key on the account page. The key (and your signing secret) are shown once.
  3. Take your first screenshot:
curl -H "Authorization: Bearer sk_live_…" \
  "https://api.shotium.com/v1/screenshot?url=https://example.com" \
  -o shot.png

Authentication

Send your API key as a Bearer token: Authorization: Bearer sk_live_…. Keys can be revoked instantly from your account page. Verify a key without spending a render: GET /v1/me returns 200 with your key's prefix and name. The signed-URL form of GET /v1/og-image uses an HMAC signature instead (see below) — no key in the URL, safe to embed publicly.

GET /v1/screenshot

Renders any public URL to an image. Responses are cached for 24 hours — identical parameter sets return the cached artifact, and cache hits are not billed.

ParamTypeDefaultNotes
urlstringrequiredhttp(s) URL, ≤2048 chars. Fragments are ignored.
widthint1280Viewport width, 1–3840
heightint800Viewport height, 1–2160
full_pageboolfalseCapture full scroll height (≤20,000px)
formatenumpngpng | jpeg | webp
qualityint801–100; lossy formats only, ignored for png
# full-page WebP at 90 quality
curl -H "Authorization: Bearer sk_live_…" \
  "https://api.shotium.com/v1/screenshot?url=https://news.ycombinator.com&full_page=true&format=webp&quality=90" \
  -o page.webp
// Node.js
const res = await fetch(
  'https://api.shotium.com/v1/screenshot?url=https://example.com&format=jpeg',
  { headers: { Authorization: `Bearer ${process.env.SHOTIUM_KEY}` } },
)
const image = Buffer.from(await res.arrayBuffer())

POST /v1/og-image

Renders a 1200×630 Open Graph image from one of five templates (browse templates & parameters). Best for build-time generation.

curl -X POST https://api.shotium.com/v1/og-image \
  -H "Authorization: Bearer sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "template": "blog",
    "params": { "title": "Shipping fast without breaking things", "author": "Ada L." },
    "format": "png"
  }' -o og.png

Body: template (id), params (template-specific, see /og-templates), optional format (png default, or jpeg).

GET /v1/og-image — signed URLs

Embed OG images directly in your HTML <head>. The URL carries an HMAC signature instead of your API key, and responses are served with Cache-Control: public, max-age=86400, immutable so Cloudflare's edge absorbs crawler traffic.

Query = template + template params + uid (your user id) + sig. Sign in your backend:

  1. Collect all query params except sig — include uid, template, and every template param.
  2. Sort keys by byte order; RFC 3986-encode each key and value; join as k=v&k=v.
  3. sig = hex(HMAC-SHA256(canonical_string, signing_secret)) — append as the sig param.
// Node.js
import { createHmac } from 'node:crypto'

// RFC 3986: percent-encode the !'()* characters encodeURIComponent leaves bare
const rfc3986 = s => encodeURIComponent(s)
  .replace(/[!'()*]/g, c => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)

const params = {
  template: 'minimal',
  title: 'Less, but better.',
  uid: 'YOUR-UID',
}
const canonical = Object.entries(params)
  .sort(([a], [b]) => (a < b ? -1 : 1))
  .map(([k, v]) => `${rfc3986(k)}=${rfc3986(v)}`)
  .join('&')
const sig = createHmac('sha256', process.env.SHOTIUM_SIGNING_SECRET)
  .update(canonical).digest('hex')

const url = `https://api.shotium.com/v1/og-image?${canonical}&sig=${sig}`
# Python
import hashlib, hmac, urllib.parse

params = {"template": "minimal", "title": "Less, but better.", "uid": "YOUR-UID"}
canonical = "&".join(
    f"{urllib.parse.quote(k, safe='')}={urllib.parse.quote(v, safe='')}"
    for k, v in sorted(params.items())
)
sig = hmac.new(SIGNING_SECRET.encode(), canonical.encode(), hashlib.sha256).hexdigest()
url = f"https://api.shotium.com/v1/og-image?{canonical}&sig={sig}"

Your uid and signing_secret are issued together with your API key. A signature is valid only for that exact parameter set — changing any value invalidates it.

Working in another language? The per-language guides carry complete signing implementations, each matched to that language's URL-encoding quirks: Python, Node.js, PHP, Ruby, Go, Java and cURL. Wiring the URL into a framework's <head>? See the OG image API guides for Next.js, Astro and Nuxt.

Integrations

n8n — install n8n-nodes-shotium from the community nodes panel. Three operations: Take Screenshot, Generate OG Image, and Generate Signed URL (computed locally — no render billed). Binary output pipes straight into S3, email or any downstream node. Setup walkthrough and workflow patterns: /integrations/n8n; source on GitHub.

Building for another platform? The REST API is small enough to wrap in an afternoon — and tell us what you're integrating so we can help.

Rate limits, caching & billing

  • Rate limit: 60 requests/minute per API key (signed URLs: per user). Exceeding returns 429 rate_limited with Retry-After.
  • Caching: identical parameter sets are cached for 24 hours and served without re-rendering. Cache hits are free — they don't count against your quota or credits, whether served from our origin or the CDN edge, and they are served even when your quota is exhausted.
  • Billing: a render is billed when a new image is rendered and successfully returned. Failed renders are never billed.
  • Edge cache: signed URL requests served from the CDN edge additionally bypass the rate limit — they never reach our servers.
  • Quota: subscription quota resets each billing period; after quota, credits are consumed. When both run out you get 429 quota_exceeded — nothing auto-charges.

Errors

All errors are application/problem+json (RFC 9457) with a stable type you can branch on:

typeHTTPWhen
unauthorized401Missing/invalid API key, or signed-URL signature failed
invalid_params400Request parameters failed validation
invalid_url400Target is not a valid http(s) URL
blocked_target403Target resolves to a blocked network range (SSRF guard)
render_timeout504Render did not complete within 30s
render_failed502Navigation/render error (DNS failure, connection refused…)
storage_error502Artifact storage unavailable — not billed
quota_exceeded429Monthly quota and credits exhausted
rate_limited429Over 60 requests/minute — includes Retry-After header
internal_error500Unexpected server error
{
  "type": "quota_exceeded",
  "title": "Monthly quota exceeded",
  "status": 429,
  "detail": "monthly quota and credits exhausted; upgrade or buy credits at https://shotium.com/pricing"
}

AI-assisted setup

Shotium is built to be integrated by AI agents as much as by hand:

  • OpenAPI 3.1 spec: the machine-readable contract for every endpoint — import it into Postman or Insomnia, generate a client in your language, or hand it to an agent as the authoritative interface.
  • Setup prompt: copy a ready-made prompt describing credentials handling, the full API contract and an integration task — paste it into Cursor, Claude Code or any coding agent. Creating a key on the account page offers the same prompt with your real credentials filled in.
  • Copy page for LLMs: the button at the top of this page copies the entire reference as Markdown.
  • /llms.txt: a machine-readable index of this site following the llms.txt convention.

Need help?

Email [email protected] — see /contact for response times.