API REFERENCE
POST /v1/screenshot
The one endpoint. POST a JSON body, get the image bytes back. Set only the options you want to change.
Endpoint
The full URL is https://api.apiscreenshot.com/v1/screenshot. Authenticate with a Bearer key (see Authentication) and send a JSON body. The response body is the raw image — content-type: image/png (or image/jpeg / image/webp):
{ "url": "https://example.com" }Source: url or html
Every request must provide exactly one of url or html — sending both, or neither, returns a 400 bad_request. Pass a live url to navigate to and capture, or send a raw html string and APIScreenshot renders exactly that markup in a real managed browser engine (ideal for receipts, OG images and email previews — no need to host a page first).
{ "url": "https://stripe.com/pricing" }url must be an absolute http/https URL. Targets that resolve to a private, loopback, link-local or cloud-metadata address (e.g. 169.254.169.254) are blocked before the browser ever dials them, returning a 400 bad_request. The html body is capped at ~2 MB.Full page, element & transparency
By default you get the current viewport. Set fullPage: true to scroll and stitch the entire scrollable page. Pass a CSS selector to capture a single element — a hero, a card, a chart — and nothing else (a selector that matches no element returns 400 selector_not_found). omitBackground: true gives you a transparent PNG where the page has no background of its own.
{ "url": "https://example.com", "fullPage": true }Clean shots: cookie banners & overlays
By default ( blockCookieBanners: true) APIScreenshot cleans the page before capturing: it clicks the reject-all / decline button on the common consent-management platforms (OneTrust, Cookiebot, Quantcast, Didomi, Usercentrics, TrustArc, Osano, CookieYes, Complianz and more), then hides a curated list of cookie/consent/overlay elements and lifts any scroll-lock the banner added — so the shot shows the page, not the pop-up. A built-in page-health guard reverts the clean-up and captures the original page if hiding ever leaves it near-empty, so you never get a blank image. Set blockCookieBanners: false to keep banners, pass hideSelectors to drop your own extra elements, or blockAds: true to also hide common ad slots.
{ "url": "https://example.com" }Viewport & retina
Set the viewport object to control the browser window the page lays out in: width and height (each 1–10000) and an optional deviceScaleFactor ( 0.1–4, default 1). Bump deviceScaleFactor to 2 or 3 for crisp retina output, or shrink the width for tablet and mobile shots.
{
"url": "https://example.com",
"viewport": { "width": 1280, "height": 800, "deviceScaleFactor": 2 }
}PNG, JPEG or WebP
Output is lossless PNG by default. Switch format to "jpeg" or "webp" for smaller files — WebP gives the best size-for-quality on the web — and tune the quality (1–100). quality applies to the lossy formats only — sending it with format: "png" (or no format) returns a 400 bad_request.
{
"url": "https://example.com",
"format": "jpeg",
"quality": 80
}Waiting for the page
Control when the capture fires. waitUntil chooses the readiness signal — "networkidle2" (the default, ≤2 in-flight requests for 500 ms), "networkidle0" (no in-flight requests), or "load" (the load event, fastest). For client-rendered content, waitForSelector blocks until a specific element appears before capturing. Both are bounded by the 20-second render budget — exceeding it returns a 504 render_timeout.
{ "url": "https://example.com", "waitUntil": "networkidle0" }Full option reference
| Option | Type | Default | Description |
|---|---|---|---|
| url | string | — | The page to capture (http/https). EXACTLY ONE of url or html is required. SSRF-checked before navigating; private/loopback/link-local/metadata targets are rejected with 400. |
| html | string | — | Raw HTML to render and capture instead of a URL. EXACTLY ONE of url or html is required. Max 2,000,000 bytes (~2 MB). |
| fullPage | boolean | false | Capture the entire scrollable page, not just the viewport. Ignored when selector is set. |
| format | "png" | "jpeg" | "webp" | "png" | Output image format. PNG is lossless; JPEG and WebP are smaller for thumbnails/previews (WebP gives the best size-for-quality on the web). |
| quality | number (1–100) | — | Compression quality for the lossy formats. Only valid when format is "jpeg" or "webp" — sending it with PNG returns 400. |
| viewport | object | 1280×720 @1x | Browser viewport: { width, height, deviceScaleFactor? }. width/height 1–10000; deviceScaleFactor 0.1–4 (default 1; set 2–3 for retina). |
| selector | string | — | CSS selector — capture just that element instead of the page. No match returns 400 selector_not_found. |
| omitBackground | boolean | false | Render a transparent background (PNG) where the page has none, instead of the default white. |
| waitUntil | "load" | "networkidle0" | "networkidle2" | "networkidle2" | When navigation/render is considered done. networkidle2 = ≤2 in-flight requests for 500ms; networkidle0 = 0; load = the load event. |
| waitForSelector | string | — | Wait for this CSS selector to appear before capturing — useful for client-rendered content. Bounded by the 20s render budget. |
| blockCookieBanners | boolean | true | Remove cookie banners, consent popups and overlays before capturing — clicks "reject all" on common CMPs, hides a curated selector list, lifts scroll-lock, and reverts to the original page if it ever over-hides to near-empty. Set false to keep banners. |
| hideSelectors | string[] | — | Extra CSS selectors to hide alongside the curated banner list (body-safe; <html>/<body> never touched). Max 50 entries, 500 chars each. |
| blockAds | boolean | false | Also hide common ad/iframe slots (best-effort) during banner removal. |
Unknown fields are ignored (forward-compatible), but a malformed known field is rejected by name with a 400 bad_request — e.g. "viewport.width" must be a number between 1 and 10000.
Response
On success: a 200 whose body is the image, with content-type: image/png, image/jpeg or image/webp (matching format) and a content-disposition: inline filename. There is no JSON envelope — write the bytes straight to a file or pipe them to storage. Each successful capture costs one credit; failed requests (any status ≥ 400) are never billed. On failure you get a JSON { error, message } body instead — see Errors.