Documentation
rev. 2026-05-19Everything you need to start taking screenshots.
Authentication
Pass your API key in the X-Api-Key header on every request.
curl https://api.scrnr.io/v1/screenshot \ -H "X-Api-Key: sk_live_xxxx"
Endpoints
/v1/screenshotTake a screenshot of any public URL.
Request body
{ "url": "https://example.com", // required "delivery": "inline" | "url", // default: inline "options": { "width": 1280, // default: 1280, max varies by plan "height": 800, // default: 800, max varies by plan "fullPage": false, // default: false "format": "png" | "jpeg" | "webp", // default: png "waitFor": "load" | "networkidle", // default: load "delay": 0, // ms, max varies by plan "blockCookieBanners": false, // default: false "headers": {"X-Custom": "value"}, // Pro plan only "retentionHours": 168 // Pro plan only, 1–720 } }
/v1/uploadUpload an existing image file (PNG, JPEG, or WebP) into your scrnr storage. The file is hosted at storage.scrnr.ioalongside captured screenshots, counts as 1 screenshot credit against your monthly quota, and obeys the same plan retention and storage rules. Useful for pushing client-side captures (browser extensions, canvas.toBlob, etc.) into the same URL namespace as scrnr-generated screenshots.
Request
multipart/form-data with a single filefield. The format is detected from the file's bytes — the client-provided Content-Type is ignored.
curl https://api.scrnr.io/v1/upload \ -H "X-Api-Key: sk_live_xxxx" \ -F "file=@./screenshot.png"
Response
201 Created, same shape as /v1/screenshot with delivery: "url":
{ "url": "https://storage.scrnr.io/screenshots/...", "expiresAt": "2026-05-20T12:00:00.000Z" // null if covered by storage allowance }
Limits: 25 MB per upload; allowed formats png, jpeg, webp. Files larger than the cap return 413; non-image bytes or unsupported formats return 400.
/v1/usageReturns current month usage and remaining quota for the authenticated API key.
Cookie Banner Bypass
Set options.blockCookieBanners: true to suppress GDPR/consent overlays before the screenshot is captured.
{ "url": "https://example.com", "options": { "blockCookieBanners": true } }
No technique covers 100% of sites. For best results combine with waitFor: "networkidle" and a small delay.
Custom Request Headers Pro
Pass extra HTTP headers to the target page — useful for capturing authenticated pages, A/B test variants, or geo-specific content. Available on the Pro plan.
{ "url": "https://example.com/dashboard", "options": { "headers": { "X-Tenant-Id": "acme", "Referer": "https://app.example.com" } } }
Limits:
- Up to 20 headers per request
- 1 KB per value, 4 KB total across all headers
- The following headers are blocked:
Host,Cookie,Authorization,Content-Length, hop-by-hop headers (Connection,Transfer-Encoding, etc.), and any header starting withProxy-
Custom File Retention Pro
Override how long the captured file is retained for a single request. Useful for ephemeral previews (set lower) or short-term archival (set higher). Available on the Pro plan; ignored when delivery: "inline".
{ "url": "https://example.com", "delivery": "url", "options": { "retentionHours": 1 // file deleted after 1 hour } }
Range: 1–720 hours (up to 30 days). Setting retentionHoursalways wins — even on captures that would otherwise be kept indefinitely under your plan's included storage. For indefinite retention without a per-request override, the Pro plan includes 5 GB of permanent storage; the +25 GB storage add-on stacks on top.
Webhook Delivery Pro
Set delivery: "webhook" to have the captured file URL pushed to your server after the screenshot completes. Configure one HTTPS endpoint per account at /dashboard/webhooks — it fires for every capture by your account, whether triggered via API key or OAuth. We'll show the HMAC secret once at creation; save it.
The HTTP response returns immediately with 202 Accepted and the screenshot ID; the webhook fires asynchronously from the worker. Failed POSTs retry with exponential backoff at 30s, 5m, 30m, 2h, 12h (5 retries, ~14.5 hours). After max attempts or any non-retryable 4xx, the delivery is marked DEAD and surfaced for manual replay in the dashboard.
Payload
{ "id": "clx9z2...", // event id; matches X-Scrnr-Event-Id header "type": "screenshot.completed", "createdAt": "2026-04-28T12:00:00.000Z", "data": { "id": "clx9z2...", // request id "url": "https://example.com", "fileUrl": "https://storage.scrnr.io/...", "expiresAt": "2026-05-05T12:00:00.000Z", "format": "png", "fileSizeBytes": 123456, "durationMs": 1234 } }
Verifying the signature
Each request includes an X-Scrnr-Signature header in the format t=<ts>,v1=<hex>. Recompute the HMAC-SHA256 of <ts>.<raw-body> using your endpoint secret and compare with v1 using a constant-time check. Reject requests where the timestamp is more than 5 minutes off.
import crypto from "node:crypto"; function verify(secret, signatureHeader, timestamp, rawBody) { const expected = crypto .createHmac("sha256", secret) .update(`${timestamp}.${rawBody}`) .digest("hex"); const provided = signatureHeader.match(/v1=([^,]+)/)?.[1] ?? ""; return ( expected.length === provided.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(provided)) && Math.abs(Date.now() / 1000 - Number(timestamp)) < 300 ); }
Headers
X-Scrnr-Event-Id— stable across retries; use as your idempotency keyX-Scrnr-Signature—t=<unix-seconds>,v1=<hex-hmac>User-Agent—scrnr-webhooks/1.0
Reply with 2xx to acknowledge. Anything else is treated as a failure (4xx are terminal except 408 / 425 / 429; 5xx and network errors retry on schedule).
Errors
All errors return JSON with an error field. Validation failures additionally include a details object from Zod.
| Status | Meaning |
|---|---|
| 400 | Invalid request body, viewport/delay over plan cap, or a Pro-only option (custom headers, custom file retention) used on a non-Pro plan. |
| 401 | Missing, invalid, or revoked API key / OAuth token. |
| 403 | URL blocked — points to a private IP, blocked domain, or otherwise fails SSRF safety checks. |
| 404 | Screenshot ID not found (only on DELETE /v1/screenshot/:id). |
| 410 | Screenshot already deleted. |
| 413 | Uploaded file exceeds the per-request size limit (25 MB). Only applies to POST /v1/upload. |
| 429 | Monthly quota reached. Response includes the limit and the resetAt timestamp. |
| 500 | Screenshot pipeline failed (target site timeout, browser crash, etc.). The request is logged and counted as FAILED, not against your quota. |
Failed individual captures (5xx) do not count toward your monthly quota. Quota counters only increment on successful captures.
MCP Integration
Add scrnr as an MCP server to use take_screenshot and get_usage directly inside your AI tool.
Sign in with your scrnr account
MCP clients that support OAuth (Claude Desktop, Claude.ai connectors, Cursor, Windsurf, etc.) can connect with no API key — they'll open a browser to sign you in and request access. Just paste the URL below; we'll do the rest. If your client doesn't support OAuth yet, the API key examples further down still work.
Claude Desktop / Claude.ai — OAuth, recommended
Settings → Connectors → Add custom connector
https://mcp.scrnr.io/mcpCursor / Windsurf — OAuth, recommended
~/.cursor/mcp.json · ~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "scrnr": { "url": "https://mcp.scrnr.io/mcp" } } }
Or use an API key
Claude Desktop — via mcp-remote (stdio bridge)
~/Library/Application Support/Claude/claude_desktop_config.json
{ "mcpServers": { "scrnr": { "command": "npx", "args": [ "mcp-remote", "https://mcp.scrnr.io/mcp", "--header", "Authorization: Bearer sk_live_xxxx" ] } } }
Cursor / Windsurf — native HTTP transport
~/.cursor/mcp.json · ~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "scrnr": { "url": "https://mcp.scrnr.io/mcp", "headers": { "Authorization": "Bearer sk_live_xxxx" } } } }
Limits
Limits are enforced per request and per calendar month based on your plan.
| Limit | Free | Basic | Pro |
|---|---|---|---|
| Screenshots / month | 100 | 3,000 | 15,000 |
| Max viewport | 1920 × 1080 | 2560 × 1440 | 3840 × 2160 |
| API keys per account | 2 | 5 | 25 |
| Max delay | 5s | 15s | 30s |
| Request timeout | 30s | 45s | 60s |
| File retention | 1 day | 3 days | 7 days |
| Storage included | — | — | 5 GB permanent |
Higher limits are available on paid plans. Manage your subscription and the optional +25 GB storage add-on from /dashboard/billing. Files captured while a storage allowance is active are kept indefinitely instead of expiring at the plan's retention window.
Quickstart
Get a screenshot in under a minute:
- 1Sign in — Go to /dashboard and sign in with your email.
- 2Create an API key — Click “New API key” — copy it immediately, it’s shown only once.
- 3Make your first request — Use the POST /v1/screenshot endpoint with your key in the X-Api-Key header.