Website screenshot API — URL to PNG, JPEG or PDF
Renders a public web page in Chromium and returns the image or PDF. The result is streamed back to you and not stored.
POST https://api.shotvik.com/v1/screenshotContent-Type: application/jsonAuthorization: Bearer <api_key>Authentication: API key in Authorization: Bearer or X-API-Key. PDF is a format of this endpoint ("format": "pdf"); there is no separate PDF endpoint and no GET variant.
Request body (JSON)
Section titled “Request body (JSON)”Unknown fields are rejected with 400 invalid_request.
| Field | Type | Default | Description |
|---|---|---|---|
url |
string, required | Public http/https URL on port 80 or 443, max 4,096 characters. Private, reserved and local addresses, other ports, non-http(s) schemes and URLs flagged as unsafe are refused. |
|
format |
png · jpeg · pdf |
png |
Output format. |
width |
integer, 100–3840 | 1280 |
Viewport width in CSS pixels. |
height |
integer, 100–3840 | 800 |
Viewport height in CSS pixels. |
full_page |
boolean | false |
Capture the full scrollable page, capped at 16,384 px height and 50 megapixels. |
device_scale_factor |
number, 1–3 | 1 |
Pixel density, e.g. 2 for retina output. |
jpeg_quality |
integer, 1–100 | 80 |
Only with format: "jpeg". Sending it with another format returns 400 invalid_request. |
wait_until |
load · domcontentloaded · networkidle · commit |
load |
Navigation event to wait for before capturing. |
delay_ms |
integer, 0–10000 | 0 |
Extra wait after the page has loaded. Counts toward the 30-second render timeout. |
dark_mode |
boolean | false |
Emulate prefers-color-scheme: dark. |
block_ads |
boolean | false |
Block requests to a list of ad and tracker hosts (request blocking only). |
block_cookie_banners |
boolean | false |
Block requests to common consent-management hosts (request blocking only). |
cache |
boolean | false |
Opt-in cache on our Hetzner servers. Nothing is stored without it. Cache hits don’t count toward your quota. |
cache_ttl |
integer, 60–2592000 | 86400 |
Cache lifetime in seconds (max 30 days, provisional). Only used with cache: true. |
Response
Section titled “Response”Success: 200 with the file as the body and Content-Type image/png, image/jpeg or application/pdf.
| Header | Meaning |
|---|---|
X-Request-Id |
Request ID (req_…). Include it when you contact support. Sent on every response, including errors. |
X-Cache |
HIT (served from your opt-in cache), MISS (rendered and stored in the cache) or BYPASS (cache not requested). |
X-Render-Time-Ms, X-Queue-Time-Ms |
Render time and time spent waiting in the render queue. |
X-Target-Status |
The HTTP status the target site returned. |
X-Usage-Used, X-Usage-Limit, X-Usage-Remaining |
Your monthly usage after this request. Also sent on cache hits, which don’t change it. |
X-Usage-Overage |
true when the render is billed as overage. Overage can’t be turned on yet (see Rate limits and plans), so for now this header isn’t sent. |
X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset |
Your per-minute rate limit, what’s left, and seconds until the window resets. |
X-Shotvik-Warnings |
Only present when fonts or images were skipped during the render. See Skipped fonts and images. |
Target site errors aren’t API errors. If the target page answers with a 4xx or 5xx status, you still get the rendered page with 200, X-Target-Status shows the target’s status, and the render counts toward your quota. See How usage is counted.
Errors: a JSON body. See Errors. Possible statuses: 400, 401, 403, 413, 429, 500, 502, 503, 504.
Example
Section titled “Example”curl https://api.shotvik.com/v1/screenshot \ -H "Authorization: Bearer $SHOTVIK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com", "format": "jpeg", "full_page": true, "jpeg_quality": 85}' \ --fail-with-body --output page.jpgMore languages: Quickstart.
Behaviour to know
Section titled “Behaviour to know”-
Honest rendering: every request’s User-Agent is the headless Chrome string followed by
ShotvikBot/0.1 (+https://shotvik.com/bot). The token is always there and can’t be removed or changed. There is no stealth mode, captcha bypass or proxies, androbots.txtisn’t consulted in v1. Sites that block bots will show their block page, which you get as the screenshot. See ShotvikBot. -
URL safety check: every request a render makes, including redirects and sub-resources, may only go to public
http/httpsaddresses on ports 80 and 443. Private, internal and metadata addresses are blocked. Pages and frames are also checked for URL reputation against two sources: the Phishing.Database list (MIT licence) and Shotvik’s own blocklist.- If the page itself (or a redirect of it) is refused, you get
403 url_blockedwith areason. - A refused sub-resource (image, font, script) is dropped from the page, and the render continues. Skipped fonts and images are reported in
X-Shotvik-Warnings. - If Shotvik’s copy of Phishing.Database is more than 48 hours old, URL renders are refused with
403 url_blockedand reasonreputation_unavailableuntil it’s updated.
- If the page itself (or a redirect of it) is refused, you get
-
Limits per render:
- render timeout of 30 s in total (including
delay_ms); - at most 26,214,400 bytes (25 MiB) downloaded and 400 requests; resources beyond the budget aren’t loaded;
- at most 5 redirects;
- WebSockets are blocked during renders.
- render timeout of 30 s in total (including
-
No storage by default: the result is streamed and not kept, unless you set
cache: true.
Skipped fonts and images
Section titled “Skipped fonts and images”When a font or image in the page isn’t loaded, the render still succeeds. The X-Shotvik-Warnings header tells you what was skipped, so a missing web font doesn’t go unnoticed:
X-Shotvik-Warnings: font:blocked:2,image:budget:1- The value is a comma-separated list of
kind:cause:count. kindisfontorimage. Other skipped resources, such as scripts or stylesheets, aren’t reported.causeisblocked(refused by the address and port rules above, or byblock_ads/block_cookie_banners) orbudget(over the per-render limit of 25 MiB or 400 requests).- The header is omitted when nothing was skipped. It’s at most 256 bytes; if the list doesn’t fit, it ends with
truncated:N. - With the opt-in cache, a cache hit returns the same warnings as the original render.