Documentation / Getting started
Getting started
This page walks you from zero to a working screenshot in a few minutes. Everything below reflects the production capture route: validation rules, headers, and JSON shapes are the same ones enforced in code.
Overview
ScreenNabster is a capture API built for automation: marketing previews, compliance archives, PDF receipts, and thumbnail pipelines. You send a URL or HTML plus optional rendering hints (viewport, full page, PDF margins, filters). We return bytes synchronously, or you can opt into a background job that delivers a signed result URL to your webhook.
All traffic is HTTPS. API keys are opaque strings starting with pk_; we store only a SHA-256 hash server-side.
Prerequisites
Authentication
Send your key on every request in the X-API-Key header. There is no query-string or cookie-based shortcut. If the header is missing, malformed (not starting with pk_), or unknown, the API responds with 401 and a JSON error body (see below).
Quota enforcement happens inside the capture handler after authentication: we atomically consume one request from your monthly allowance before any render work starts, which prevents race conditions when you burst traffic.
Endpoint & methods
The capture API lives at a single path on your app host (no separate subdomain required):
POST https://screennabster.vercel.app/api/v1/capture
For this deployment, the public origin is https://screennabster.vercel.app. Set NEXT_PUBLIC_APP_URL in your environment so documentation examples and redirects stay aligned.
POST accepts a JSON object of options. Booleans and numbers are native JSON types; arrays arrive as JSON arrays.
GET accepts the same fields as query parameters. Strings stay strings; booleans use true / false; arrays such as filters use comma-separated values; headers and cookies must be JSON-encoded strings in the query string. When in doubt, prefer POST.
Your first capture
The smallest valid request supplies either url or html. Defaults produce a 1280×800 viewport PNG. Replace the placeholder key with a real key from your dashboard.
curl -X POST "https://screennabster.vercel.app/api/v1/capture" \
-H "Content-Type: application/json" \
-H "X-API-Key: pk_live_your_key_here" \
-d "{\"url\":\"https://example.com\",\"output\":\"png\"}" \
--output screenshot.pngEquivalent GET (handy for quick tests or CDNs that only issue GETs):
curl -G "https://screennabster.vercel.app/api/v1/capture" \
--data-urlencode "url=https://example.com" \
-H "X-API-Key: pk_live_your_key_here" \
--output screenshot.pngFrom Node or a browser extension backend:
const res = await fetch("https://screennabster.vercel.app/api/v1/capture", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.SCREENNABSTER_API_KEY,
},
body: JSON.stringify({
url: "https://example.com",
output: "png",
page_full: true,
}),
});
if (!res.ok) {
const err = await res.json();
console.error(err.code, err.error, err.status);
process.exit(1);
}
const buffer = Buffer.from(await res.arrayBuffer());
// write buffer to disk or upload to your storageSynchronous responses
When background is false (the default), a successful capture returns the raw bytes of the asset. The Content-Type header matches the format you requested (for example image/png, application/pdf, or text/html). We also send X-ScreenNabster-Duration with the server-side timing in milliseconds so you can monitor latency.
HTML output is always returned with Content-Disposition: attachment so browsers save a file instead of executing the markup from our API origin — a deliberate safety measure.
Setting background: true changes the response to JSON: { "job_id", "status", "message" }. The worker finishes asynchronously and calls your callback_url with a signed link to the file in storage. See the API reference and the async guide.
Errors
Failed requests return JSON (never HTML) with three fields: error (human-readable message), code (stable machine identifier), and status (duplicate of the HTTP status for convenience in some clients). Validation failures pack multiple Zod issues into the error string separated by semicolons.
{
"error": "Monthly request limit reached. Upgrade your plan at https://screennabster.com/billing",
"code": "RATE_LIMIT_EXCEEDED",
"status": 429
}A full list of codes lives in the reference.
Quotas & billing
Each successful authentication path into the capture handler attempts to consume exactly one request from your monthly pool before work begins. If you are out of quota, you receive 429 with RATE_LIMIT_EXCEEDED. Background jobs consume the same quota at enqueue time.
Upgrade or manage payment methods from the Billing page. Usage history per key is available under Usage.
How it fits together
Vercel (Next.js) terminates TLS, validates keys, enforces SSRF rules on URLs, checks quota, and forwards render work to the worker. Supabase holds users, keys, usage logs, and queued jobs.
Render worker runs Playwright on a VPS. It loads the page, applies your options, and returns binary data to the gateway (sync) or posts back to an internal route that uploads to private storage and fires your webhook (async).
Next step: skim the guides for your use case, or jump straight to the field-by-field reference.