Skip to Content

screenshots

capture viewport or full-page screenshots of any web page. screenshots are returned as plain public urls. when a screenshot is delivered, the request uses the 5-credit screenshot engine base before the proxy multiplier.

two behaviors to design around:

  • requesting a screenshot always routes the scrape through browser rendering — this adds latency compared to an http scrape.
  • in rare cases a screenshot can’t be captured. when that happens, the rest of your requested outputs are delivered as normal and the screenshot field is simply left out — so always check for screenshot rather than assuming it’s there. a request for only a screenshot fails with an error instead — see when no screenshot can be delivered.

screenshot types

typedescription
viewportcaptures what’s visible in the browser viewport
full_pagecaptures the entire page, handling long pages adaptively

pass the type inside the extract.screenshot object when calling POST /api/scrape or POST /api/scrape/async.

viewport screenshot

the simplest option. captures exactly what a user would see in the browser window without scrolling. the default viewport is 1280×720 (desktop); it’s 390×844 in mobile device mode, and you can override it with a custom viewport.

curl -X POST https://api.crawlbrulee.com/api/scrape \ -H "Authorization: Bearer $CRAWLBRULEE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "extract": { "screenshot": { "type": "viewport" } } }'

response (condensed):

{ "screenshot": { "url": "https://cdn.crawlbrulee.com/scrape_assets/media/images/abc123/screenshot.webp", "type": "viewport", "properties": { "file_name": "screenshot.webp", "mime": "image/webp", "width": 1280, "height": 720, "viewport": { "width": 1280, "height": 720, "device_scale_factor": 1 } } } }

full-page screenshot

captures the entire page in a single image, handling long pages adaptively.

curl -X POST https://api.crawlbrulee.com/api/scrape \ -H "Authorization: Bearer $CRAWLBRULEE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "url": "https://example.com", "extract": { "screenshot": { "type": "full_page" } } }'

response (condensed):

{ "screenshot": { "url": "https://cdn.crawlbrulee.com/scrape_assets/media/images/def456/screenshot.webp", "type": "full_page", "properties": { "file_name": "screenshot.webp", "mime": "image/webp", "width": 1280, "height": 4200, "viewport": { "width": 1280, "height": 720, "device_scale_factor": 1 } } } }

screenshots come back as WebP. when the finished image is more than 16,383 device pixels on either side — width or height — we encode it as jpeg (image/jpeg) instead, since 16,383 is WebP’s per-side maximum. an image sitting exactly at 16,383 still encodes as WebP. screenshots are never returned as png.

slicing full-page screenshots

combine type: "full_page" with a slice action in actions_after to also get the page split into horizontal tiles — useful when you need to feed a long screenshot to something with a smaller per-image size limit (e.g. an image model). most legitimate pages are well under 10,000px tall, so in practice slicing produces only a handful of tiles.

{ "url": "https://example.com", "extract": { "screenshot": { "type": "full_page", "actions_after": [{ "type": "slice", "height": 800 }] } } }

the response includes a slices array alongside the full screenshot:

{ "screenshot": { "url": "https://cdn.crawlbrulee.com/scrape_assets/media/images/ghi789/screenshot.webp", "type": "full_page", "properties": { "file_name": "screenshot.webp", "mime": "image/webp", "width": 1280, "height": 3200, "viewport": { "width": 1280, "height": 720, "device_scale_factor": 1 } }, "slices": [ { "row_nr": 0, "url": "https://cdn.crawlbrulee.com/scrape_assets/media/images/ghi789/screenshot_slice_0.webp", "type": "slice", "properties": { "file_name": "screenshot_slice_0.webp", "mime": "image/webp", "width": 1280, "height": 800, "viewport": { "width": 1280, "height": 720, "device_scale_factor": 1 } } }, { "row_nr": 1, "url": "https://cdn.crawlbrulee.com/scrape_assets/media/images/ghi789/screenshot_slice_1.webp", "type": "slice", "properties": { "file_name": "screenshot_slice_1.webp", "mime": "image/webp", "width": 1280, "height": 800, "viewport": { "width": 1280, "height": 720, "device_scale_factor": 1 } } } ] } }

screenshot slicing adds a flat +1 credit when the requested slice variant is generated, regardless of how many image tiles it contains. minimum slice height is 500px, and only one actions_after entry is allowed. the number of slices produced is ceil(image_height / slice_height), capped at 20 per image. nothing is lost at the cap — the final slice carries the whole remaining height, so it comes back taller than the tile height you asked for. see screenshot slices for exactly how slicing interacts with caching and credits.

pre-screenshot actions

actions_before lets you wait or scroll before taking the screenshot. you can chain up to 5 actions, with a combined total wait of 20,000ms and a combined total scroll distance of 50,000px across all actions — exceeding either limit returns a validation error.

{ "url": "https://example.com", "extract": { "screenshot": { "type": "viewport", "actions_before": [ { "type": "wait", "ms": 2000 }, { "type": "scroll", "pixels": 500 } ] } } }
actionfieldsdescription
waitms (integer)wait for the specified milliseconds before continuing
scrollpixels (integer)scroll down by the specified pixels (use a negative value to scroll up)

useful for:

  • waiting for lazy-loaded content to appear
  • scrolling to trigger animations or infinite-scroll loads
  • giving cookie banners or overlays time to dismiss

actions_before entries with a non-zero wait or scroll disable caching for that request — each call runs a fresh scrape. an actions_before array with only zero-value actions (or an empty array) doesn’t affect caching.

custom viewport

override the default viewport dimensions to match the device or resolution you need. if you pass a viewport object, both width and height are required.

{ "url": "https://example.com", "extract": { "screenshot": { "type": "viewport", "viewport": { "width": 1920, "height": 1080, "device_scale_factor": 2 } } } }
fieldtyperequireddefaultdescription
widthintegeryesviewport width in pixels. integer in the range 1610000
heightintegeryesviewport height in pixels. integer in the range 1610000
device_scale_factornumberno1pixel density multiplier, 13 (fractional values allowed). set to 2 for retina-quality

values outside these ranges are rejected with a 400 at the api boundary. when viewport is omitted entirely, the default comes from device_mode: 1280x720 for desktop, 390x844 for mobile.

how device_scale_factor is applied

an in-range device_scale_factor isn’t always used exactly as sent. the captured image is width × device_scale_factor device pixels wide, and the browser can’t rasterise an image side longer than 16,383 device pixels. when your combination would cross that, we snap the scale factor down to the next standard device pixel ratio — 3, 2.5, 2, 1.75, 1.5, 1.25, 1 — until the width fits, rather than hand you a silently cropped image. standard steps keep the emulated ratio looking like real hardware.

so width: 1920 with device_scale_factor: 3 is captured as asked (5,760px wide), while width: 10000 with device_scale_factor: 3 drops to 1.5 (15,000px wide, the widest standard step that fits). the factor we actually captured with is echoed back in properties.viewport.device_scale_factor — read it there when pixel density matters, instead of assuming the requested value was used.

device mode

emulate desktop or mobile browsing. device mode controls the user agent string and the default viewport size.

valuebehavior
"desktop" (default)standard desktop user agent; default viewport 1280x720
"mobile"mobile user agent; default viewport 390x844
{ "url": "https://example.com", "extract": { "screenshot": { "type": "viewport", "device_mode": "mobile" } } }

combine with a custom viewport to test specific mobile resolutions.

cleanup

by default, we remove ads, cookie banners, and popups before capturing the screenshot.

{ "url": "https://example.com", "extract": { "screenshot": { "type": "viewport", "cleanup": { "ads_and_popups": true } } } }

set ads_and_popups to false to capture the page as-is, overlays and all. the default is true.

request parameters

full reference for the extract.screenshot object.

parametertyperequireddefaultdescription
typestringyes"viewport" or "full_page"
viewportobjectno1280x720 (desktop) / 390x844 (mobile)custom viewport dimensions — width and height both required if set
device_modestringno"desktop""desktop" or "mobile"
cleanupobjectno{ ads_and_popups: true }page cleanup before capture
actions_beforearrayno[]up to 5 pre-screenshot actions (wait, scroll); total wait ≤ 20,000ms, total scroll ≤ 50,000px
actions_afterarrayno[]up to 1 post-screenshot action (slice, minimum height 500px, at most 20 slices — the last one absorbs any remaining height)

response schema

the screenshot object returned in the scrape response. in the rare case a requested screenshot can’t be captured, this field is simply left out and the rest of your requested outputs are returned as normal — unless the screenshot was the only thing you asked for, in which case the request fails instead (see when no screenshot can be delivered).

fieldtypedescription
urlstringpublic url to the screenshot image
typestring"viewport" or "full_page"
properties.file_namestringgenerated filename (e.g., screenshot.webp)
properties.mimestringimage/webp, or image/jpeg when the image is over 16,383px on either side
properties.widthnumberimage width in pixels
properties.heightnumberimage height in pixels
properties.viewportobjectviewport used: { width, height, device_scale_factor }
slicesarraypresent when a slice action was requested via actions_after
slices[].row_nrnumberzero-based index of the slice
slices[].urlstringpublic url to the slice image
slices[].typestringalways "slice"
slices[].propertiesobjectsame shape as the top-level properties

warnings

a response can include a top-level warnings array of stable string codes. for screenshots, that’s screenshot_truncated: the page was taller than the maximum capture height (15,000px), so the image — and any slices — stop at that point instead of covering the full page. warnings are stored with the result, so a cache hit reports the same cap the original capture did.

screenshots aren’t the only thing that can be capped — see limits and truncation for the full list of warning codes and the caps behind them.

{ "screenshot": { "url": "https://cdn.crawlbrulee.com/scrape_assets/media/images/jkl012/screenshot.webp", "type": "full_page", "properties": { "file_name": "screenshot.webp", "mime": "image/webp", "width": 1280, "height": 15000, "viewport": { "width": 1280, "height": 720, "device_scale_factor": 1 } } }, "warnings": ["screenshot_truncated"] }

retention

screenshot urls remain valid for 60 days after the scrape. download the image before then if you need to keep it longer.

when no screenshot can be delivered

in rare cases a screenshot can’t be delivered. what happens depends on what else you requested:

  • the request also asks for other outputs (markdown, links, …) — the scrape succeeds: every other requested field is delivered as normal, the screenshot field is left out, and no error is raised. check for screenshot instead of assuming it’s there.
  • the request asks only for a screenshot — instead of returning an empty 200, the request fails:
    • the content type can’t be screenshotted (json, plain text, markdown, xml) → 422 with error name unsupported_screenshot_output.
    • the page is screenshottable but the capture failed → 500.

in both failure cases you’re not billed — the failure is detected before any credits are consumed. these semantics apply to sync scrapes (fresh and cached alike) and to async results fetched from GET /api/scrape/result/{job_id}. see errors for the error format.

combining with other extraction options

you can request screenshots alongside any other extraction option in a single call.

{ "url": "https://example.com", "extract": { "markdown": true, "links": true, "screenshot": { "type": "full_page" } } }

a delivered screenshot costs 5 credits on basic proxy and 25 on advanced. generating the requested slice variant adds 1 flat credit after the multiplier. if an optional screenshot is not delivered, the request is billed at the engine that delivered the base result. see credits & pricing for the complete formula.