Documentation / API reference
API reference
Single capture endpoint, exhaustive option list, webhook contract, and errors. Values mirror captureSchema in the application source.
Overview
ScreenNabster exposes /api/v1/capture for all capture types. The same option object works for GET and POST; only the encoding differs. Successful sync responses are raw bytes. Errors are JSON with error, code, and status.
Base URL for this environment: https://screennabster.com
All capture modes are also checked by ScreenNabster's prohibited-use policy before quota is consumed or rendering begins. Blocked targets, suspended accounts, or restricted high-risk features return 403 with a policy-specific error code.
ScreenshotOne-style aliases are documented in the migration guide.
Authentication
Header: X-API-Key: pk_…. Required on every call. Keys are created in the dashboard; inactive or unknown keys return 401.
For public GET links such as <img src>, use access_key, expires (Unix seconds), plus signature. The access key is the public ak_live_* identifier from the dashboard, not the private pk_* API key. The signature is HMAC-SHA256 over the exact query string without the signature parameter, using the signing secret shown once when creating the API key. The URL must not be expired: expires must be in the future and within 15 minutes of the server clock when the request arrives.
curl -X POST "https://screennabster.com/api/v1/capture" \
-H "X-API-Key: pk_live_your_key" \
-H "Content-Type: application/json" \
-d "{\"url\":\"https://example.com\"}" \
--output out.pngEndpoint
POST https://screennabster.com/api/v1/capture
GET https://screennabster.com/api/v1/capture?…
POST body: JSON object. GET: query parameters with comma-separated arrays and JSON strings where noted in the parameter tables. ScreenNabster accepts its native option names and ScreenshotOne-compatible aliases for common migration paths.
Bulk captures
Use POST /api/v1/capture/bulk for up to 20 synchronous captures. Shared options are merged into each request, and per-request values win. The optional optimize flag (body root, options, or per-request) is accepted for ScreenshotOne migration compatibility and is currently a no-op.
{
"execute": true,
"options": {
"format": "png",
"viewport_width": 1440
},
"requests": [
{ "url": "https://example.com" },
{ "url": "https://example.org", "format": "webp" }
]
}Set execute=false to generate signed public capture URLs instead of rendering immediately. Generated URLs include access_key, expires, and signature, so they can be embedded without an X-API-Key header.
curl -X POST "https://screennabster.com/api/v1/capture/bulk" \
-H "X-API-Key: pk_live_your_key" \
-H "Content-Type: application/json" \
-d "{\"execute\":false,\"options\":{\"format\":\"png\"},\"requests\":[{\"url\":\"https://example.com\"}]}"Target
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
url | string (URL) | one of url/html/markdown | — | Public http(s) page to load. Private IPs, localhost, and metadata hosts are rejected. |
html | string | one of url/html/markdown | — | Raw HTML document to render in the browser instead of navigating to url. |
markdown | string | one of url/html/markdown | — | Markdown document to render in the browser instead of navigating to url. |
Output
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
output | png | jpg | webp | avif | tiff | gif | pdf | html | markdown | optional | png | Rendered MIME type / format. Alias: format (jpeg maps to jpg). |
response_mode | binary | json | empty | optional | binary | Return raw bytes, base64 JSON metadata, or no body. Alias: response_type=by_format|json|empty. |
metadata_icon | boolean | optional | false | When response_mode=json, add resolved favicon URL (`metadata.icon`). |
metadata_image_size | boolean | optional | false | When response_mode=json, add og:image width/height if the image loads in-page. |
quality | number 0–100 | optional | 80 | Lossy image quality; ignored for PNG. |
thumb_width | positive int | optional | — | Resize output to this width. |
thumb_height | positive int | optional | — | Resize output to this height. |
format | alias | optional | — | ScreenshotOne-compatible alias for output. |
response_type | alias | optional | — | ScreenshotOne-compatible alias for response_mode. |
image_quality | alias | optional | — | Alias for quality. |
image_width | alias | optional | — | Alias for thumb_width. |
image_height | alias | optional | — | Alias for thumb_height. |
Viewport & device
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
screen_width | positive int | optional | 1280 | Viewport width in CSS pixels. |
screen_height | positive int | optional | 800 | Viewport height in CSS pixels. |
viewport_width | alias | optional | — | Alias for screen_width. |
viewport_height | alias | optional | — | Alias for screen_height. |
screen_landscape | boolean | optional | false | Swap width/height for landscape layout. |
device | string | optional | — | Playwright device preset name (see worker device catalog) or legacy aliases iPad, Galaxy S21. |
viewport_device | alias | optional | — | ScreenshotOne-compatible alias for device. |
device_scale_factor | number 0.5–4 | optional | — | Override device pixel ratio when emulating. |
Page behaviour
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page_full | boolean | optional | false | Capture the full scrollable page. |
full_page | alias | optional | — | Alias for page_full. |
page_full_scroll | boolean | optional | — | Scroll through the page before a full-page capture to trigger lazy loading. |
full_page_scroll | alias | optional | — | Alias for page_full_scroll. |
page_full_scroll_delay | int ms | optional | — | Delay between lazy-scroll steps. |
full_page_scroll_delay | alias | optional | — | Alias for page_full_scroll_delay. |
page_full_scroll_by | positive int | optional | — | Pixels per lazy-scroll step. |
full_page_scroll_by | alias | optional | — | Alias for page_full_scroll_by. |
page_full_max_height | positive int | optional | — | Cap full-page height for long or infinite pages. |
full_page_max_height | alias | optional | — | Alias for page_full_max_height. |
full_page_algorithm | default | by_sections | optional | default | Compatibility flag. by_sections is accepted; current worker still uses Playwright full-page capture. |
wait | int ms 0–10000 | optional | 0 | Extra sleep before screenshot. |
delay | alias | optional | — | ScreenshotOne-compatible alias for wait. |
wait_until | load | domcontentloaded | networkidle | commit | optional | load | Playwright navigation wait condition. |
wait_for_selector | string (CSS) | optional | — | Wait until this selector matches before capture. |
scroll_to | number | optional | — | Scroll vertically to this Y offset before capture. |
selector | string (CSS) | optional | — | Clip capture to the first matching element’s bounding box. |
selector_algorithm | default | clip | optional | default | Use element screenshot or page clip around the element. |
selector_scroll_into_view | boolean | optional | true | Scroll selected element into view before capture. |
capture_beyond_viewport | boolean | optional | — | Pass through to Playwright screenshot captureBeyondViewport. |
clip_x | number | optional | — | Manual clip rectangle origin X (viewport coords). |
clip_y | number | optional | — | Manual clip rectangle origin Y. |
clip_w | number | optional | — | Manual clip width. |
clip_h | number | optional | — | Manual clip height. |
click_selector | string (CSS) | optional | — | Click element before capture. |
click | alias | optional | — | Alias for click_selector. |
hover_selector | string (CSS) | optional | — | Hover element before capture. |
hover | alias | optional | — | Alias for hover_selector. |
error_on_click_not_found | boolean | optional | true | Fail if click_selector missing. |
error_on_hover_not_found | boolean | optional | true | Fail if hover_selector missing. |
hide_selectors | string[] | optional | [] | Selectors to hide (display:none) before capture. GET: comma-separated list. |
Appearance & scripting
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
theme_dark | boolean | optional | false | Prefer dark color scheme. |
dark_mode | alias | optional | — | Alias for theme_dark. |
transparent | boolean | optional | false | Transparent background where supported (e.g. PNG). |
inject_css | string | optional | — | CSS injected before capture. |
styles | alias | optional | — | Alias for inject_css. |
inject_js | string | optional | — | JavaScript executed in page before capture. |
scripts | alias | optional | — | Alias for inject_js. |
PDF options
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
pdf_format | A0 | A1 | A2 | A3 | A4 | A5 | A6 | Letter | Legal | Tabloid | optional | A4 | Printed page size. Lowercase values and pdf_paper_format alias are accepted. |
pdf_landscape | boolean | optional | false | Landscape orientation. |
pdf_print_background | boolean | optional | true | Include background graphics in PDF. |
pdf_fit_one_page | boolean | optional | false | Fit document dimensions into one PDF page when possible. |
media_type | screen | print | optional | CSS media emulation before PDF generation. | |
pdf_margin | string | optional | — | Shorthand margin (CSS length). |
pdf_margin_top | string | optional | — | Top margin override. |
pdf_margin_right | string | optional | — | Right margin override. |
pdf_margin_bottom | string | optional | — | Bottom margin override. |
pdf_margin_left | string | optional | — | Left margin override. |
pdf_scale | number 0.1–2 | optional | — | Scale factor for PDF output. |
Blocking & filters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
strip_ads | boolean | optional | false | Block common ad network hosts. |
block_ads | alias | optional | — | ScreenshotOne-compatible alias for strip_ads. |
filters | cookie_banners | ads | popups | chats | trackers | optional | [] | Content filters applied by the worker. GET: comma-separated. |
block_cookie_banners | boolean | optional | false | ScreenshotOne-style: append cookie_banners to filters when true. |
block_chats | boolean | optional | false | Append chats to filters when true. |
block_trackers | boolean | optional | false | Append trackers to filters when true. |
block_banners_by_heuristics | boolean | optional | false | Heuristic banner blocking; sets strip_ads when true (native strip_ads still wins if set). |
block_resources | image | stylesheet | font | media | script | xhr | fetch | optional | [] | Playwright resource types to abort. GET: comma-separated. |
Geolocation
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
geo_lat | number −90…90 | optional | — | Emulated latitude. |
geo_lng | number −180…180 | optional | — | Emulated longitude. |
geolocation_latitude | alias | optional | — | ScreenshotOne-compatible alias for geo_lat. |
geolocation_longitude | alias | optional | — | ScreenshotOne-compatible alias for geo_lng. |
geo_accuracy | positive number | optional | — | Accuracy in meters. |
Browser & HTTP
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
locale | string | optional | — | Playwright context locale (en-US). If both locale and timezone_id are omitted on a URL capture, the server infers them from hostname (apple.de → Germany, paypal.com → en-US). |
timezone_id | string (IANA) | optional | — | Browser timezone; inferred together with locale from URL hostname patterns when omitted. |
dismiss_geo_prompts | boolean | optional | true | When true (default), the worker dismisses notable first‑party overlays: Apple geo storefront ribbons on apple.com, and on google.com / www.google.* clicks through Google’s consent interstitial (“Before you continue…” — CMP often loads in a consent.google iframe). That screen is triggered by egress IP/geo; query params like ?hl=en&gl=us tune language/UI copy but do **not** remove the curtain—as your screenshot illustrates. Prefer US egress (`proxy_region=us` when US_PROXY_* is set) to reduce EU-style overlays, or Growth+ filters for many third‑party CMPs. |
egress_alignment | default | caller | optional | — | caller: derive proxy_region per request from CDN geo headers (Vercel x-vercel-ip-country, Cloudflare cf-ipcountry)—EU-ish → eu, rest-of-world → us. Ignored if proxy_region is set. Stripped before the worker receives the payload. |
proxy_region | us | eu | auto | optional | — | Starter+: route egress through proxies set as US_PROXY_* / EU_PROXY_* on the worker. auto picks US egress for generic english-commercial hostnames (.com etc.) when env is configured. |
browser_agent | string | optional | — | Custom User-Agent header. |
auth_user | string | optional | — | HTTP Basic username (not stored on async jobs). |
auth_pass | string | optional | — | HTTP Basic password (not stored on async jobs). |
headers | Record<string, string> | optional | — | Extra request headers. Authorization, Cookie, Host, X-Forwarded-For, and worker secrets are rejected. GET: JSON object string. |
Caching & async
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
cached | boolean | optional | false | Use cached render when available. Alias: cache. |
cache_ttl | int seconds 14400–2592000 | optional | — | Signed cache URL lifetime; defaults to 4 hours. |
cache_key | string | optional | — | Custom key component for distinct cached variants. |
background | boolean | optional | false | Queue job; respond with job_id immediately. |
callback_url | string (URL) | optional | — | HTTPS URL for webhook when background job completes. Failed events require webhook_errors=true. |
webhook_errors | boolean | optional | false | Send capture.failed webhook payloads when worker rendering fails. |
external_identifier | string | optional | — | Alphanumeric tracking value included in webhook body and X-ScreenNabster-External-Identifier header. |
Internal
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
storage_bucket | string | optional | — | Reserved for platform use. Do not depend on it in customer integrations. |
Async jobs & webhooks
Set background: true. The API responds immediately with:
{
"job_id": "uuid",
"status": "queued",
"message": "Render queued. Results will be sent to your callback_url."
}Basic auth credentials are stripped before the job is persisted — only the running worker session receives them. When rendering finishes, our backend uploads the binary to private storage, creates a time-limited signed URL, and POSTs to your callback_url if provided. Failed render events are sent only when webhook_errors is true. If a job cannot be persisted or accepted by the worker after quota is consumed, quota is released.
Webhook headers:
Content-Type: application/jsonX-ScreenNabster-Signature: sha256=<hex>— HMAC-SHA256 of the raw body usingSCREENNABSTER_WEBHOOK_SIGNING_SECRETon our side; verify with the same secret on yours (timing-safe compare).X-ScreenNabster-External-Identifier— present when you setexternal_identifier.
Success payload shape:
{
"event": "capture.completed",
"job_id": "uuid",
"created_at": "2026-04-11T12:00:00.000Z",
"result_url": "https://…signed…",
"external_identifier": "customer-job-7"
}Failure payload shape:
{
"event": "capture.failed",
"job_id": "uuid",
"created_at": "2026-04-11T12:00:00.000Z",
"error": "Worker error message",
"error_code": "RENDER_ERROR",
"external_identifier": "customer-job-7"
}Webhook delivery is attempted once. Only 2xx responses mark callback_sent_at; non-2xx responses and network failures count as attempts but are not retried automatically yet.
Error codes
JSON body: { "error": string, "code": string, "status": number }. The HTTP status code matches status.
| HTTP | Code | Meaning |
|---|---|---|
401 | MISSING_API_KEY | No X-API-Key header was sent. |
401 | INVALID_API_KEY_FORMAT | Key must start with pk_. |
401 | INVALID_API_KEY | Key not found, revoked, or inactive. |
401 | SIGNATURE_REQUIRED | Public access_key GET request is missing a signature. |
401 | SIGNATURE_INVALID | Signature does not match the request query string. |
401 | EXPIRES_REQUIRED | Signed GET URLs must include a valid Unix expires parameter covered by the signature. |
401 | SIGNATURE_EXPIRED | The signed URL expires value is in the past (with small clock skew). |
401 | EXPIRES_INVALID | The expires value is too far in the future; regenerate the URL. |
401 | SIGNATURE_NOT_CONFIGURED | The API key predates signed URLs; create a new key to get a signing secret. |
400 | CALLBACK_URL_BLOCKED | The callback URL failed SSRF checks or must use HTTPS. |
400 | INVALID_BODY | POST body is not valid JSON. |
400 | VALIDATION_ERROR | Options failed validation (see error text for Zod paths). |
403 | PLAN_REQUIRED | The requested option requires a higher plan tier. |
403 | POLICY_VIOLATION | The request uses a feature or pattern disallowed by account policy. |
403 | PROHIBITED_TARGET | The target URL or domain is blocked by the prohibited-use policy. |
403 | ACCOUNT_REVIEW_REQUIRED | The account must be reviewed before more captures can be processed. |
403 | ACCOUNT_SUSPENDED | The account is suspended due to abuse, billing, or policy reasons. |
429 | RATE_LIMIT_EXCEEDED | Monthly request quota for this key/user is exhausted. |
500 | QUEUE_ERROR | Background job could not be written to the queue. |
502 | QUEUE_ERROR | Worker rejected or could not accept a background job. |
500 | RENDER_ERROR | The render worker failed or timed out for this capture. |