Documentation
Everything you need to generate screenshots and OG images from code. Base URL for all requests: https://api.shots.dev
Quickstart
Get your first image in under two minutes. No SDK required.
1. Get an API key
2. Render your first OG image
3. Take a screenshot
Prefer trying before reading? The live demo hits the real API in your browser — no key required.
Authentication
Every request (except POST /v1/keys and GET /v1/templates) needs your API key. Send it as a Bearer token:
For contexts that can't set headers — <img> tags, og:image meta tags, email HTML — pass the key as a query parameter instead:
Keep keys server-side. The ?api_key= form is convenient but exposes your key in URLs and logs. For high-traffic pages, render once, cache the PNG on your own CDN, and serve that instead.
Endpoints
Five endpoints. That's the whole API.
Create an API key. One call, one key — no dashboard, no email verification loop.
| Field | Type | Description |
|---|---|---|
| emailrequired | string | Where we send plan and billing updates. Never shared, never marketed to. |
Returns 201 with {"key": "sk_live_…", "tier": "free", "created_at": "…"}. The key is shown once — store it in your secrets manager immediately.
List all templates with their fields, defaults, and character limits. Use this to build dynamic forms — it's what powers our live demo.
Returns 200 with {"templates": [{slug, name, description, category, width, height, fields: [{key, label, type, default, maxLength}]}]}.
Render a template to a 1200×630 PNG. Send fields as a JSON object; anything you omit falls back to the template's default. Also available as GET /v1/og/:slug?title=…&kicker=… with fields as query parameters.
| Parameter | Type | Description |
|---|---|---|
| :slugrequired | path | Template slug, e.g. blog-classic. See GET /v1/templates or the gallery. |
| {field}optional | json / query | One key per template field. Values longer than maxLength are rejected with 422. |
Returns 200 with Content-Type: image/png — raw image bytes, ready to save, hotlink, or pipe into your build. Unknown slugs return 404.
Capture any public URL on our managed Chromium fleet. Ads and cookie banners are blocked automatically; pages render at 2× retina by default.
| Parameter | Type | Description |
|---|---|---|
| urlrequired | string | Fully-qualified URL to capture (https://…). Must be publicly reachable. |
| widthoptional | integer | Viewport width in px. Default 1280, max 1920. |
| heightoptional | integer | Viewport height in px. Default 720. Ignored when full_page=true. |
| full_pageoptional | boolean | Capture the entire scrollable page instead of just the viewport. Default false. |
| waitoptional | integer | Extra milliseconds to wait after load (for JS-heavy pages). Default 500, max 10000. |
Returns 200 with Content-Type: image/png. Unreachable URLs return 422.
Check your current billing period usage. Poll this to build usage meters or alerting — it's cheap and never rate-limited below your plan's normal limits.
Returns 200 with {"tier": "starter", "period": "2026-10", "images_used": 412, "images_limit": 2500, "resets_at": "2026-11-01T00:00:00Z"}.
Rate limits
Limits are per API key, per minute. Exceeding them returns 429 with a Retry-After header (in seconds). Beta limits are conservative; paid limits increase at launch.
| Plan | Requests / minute | Monthly images |
|---|---|---|
| Free | 10 | 100 |
| Starter ($9) | 60 | 2,500 |
| Pro ($19) | 60 | 15,000 |
Hitting limits in production? Render once and cache the PNG on your CDN — OG images rarely change per deploy. Most customers make 90% fewer calls after adding a cache layer.
Error codes
Errors return JSON with a machine-readable code and a human-readable message:
| Status | Code | What it means | What to do |
|---|---|---|---|
| 400 | invalid_fields | Malformed request body or query. | Check the parameter types in the reference above. |
| 401 | invalid_key | Missing, malformed, or revoked API key. | Verify the Authorization header; create a new key if needed. |
| 402 | quota_exceeded | Monthly image quota used up. | Wait for reset or upgrade via GET /v1/usage. |
| 404 | template_not_found | No template with that slug. | List slugs via GET /v1/templates. |
| 422 | validation_error | A field exceeds maxLength, or the screenshot URL is unreachable. | Trim the field or check the URL. |
| 429 | rate_limited | Too many requests. | Back off for Retry-After seconds; add caching. |
| 500 | render_failed | Our renderer hit an internal error. | Retry once; contact support if it persists. |
FAQ
How do I rotate a compromised key?
POST /v1/keys, deploy it, then ask support to revoke the old one. Self-serve revocation is coming at launch.Can I hotlink generated images directly?
?api_key=, but don't — it leaks your key in referrer headers and logs. Render once, cache on your CDN, serve the cached copy.What fonts are available in templates?
Do screenshots work with JS-heavy SPAs?
wait parameter (up to 10s) before capturing.Is there an SDK?
fetch or requests is all you need (see Quickstart). Official Node and Python SDKs ship at launch.Ready? Grab a key and render your first image in two minutes — or poke at the live demo first.