Instant Snap API

Request parameters

Everything POST /captures accepts. Only url is required; every other field has a published default, and the API tells you what those defaults are rather than making you trust this page.

How fields are read

Presence, not truthiness

Every optional field is nullable and none has a built-in value in the request model, so the API can tell “not sent” from “sent as false”. That distinction matters most for the two blockers, which default to on:
Three requests, two outcomes
// Ads blocked - block_ads defaults to true
{ "url": "https://example.com" }

// Ads NOT blocked - the field was sent as false
{ "url": "https://example.com", "block_ads": false }

// Ads blocked - null is treated as "not sent"
{ "url": "https://example.com", "block_ads": null }

Clamp versus reject

An omitted numeric field is filled from the defaults and never refused. A value you do send outside its allowed range is refused with validation_failed, and the message names the field and the bound. The API will not quietly substitute the nearest legal value.

Field names are snake_case. Unknown fields are ignored rather than rejected, so a request written against a newer version of this page still works — but a misspelled field is silently ignored too, which is worth remembering when a setting appears to have no effect.

Core fields

FieldTypeDefaultDescription
urlRequiredstring—The page to capture. http or https only, at most 2048 characters. The only field with no default.
response_typestringjsonby_format, json or cloud_storage. Decides the shape of the whole response, not a detail within it.
asyncbooleanfalseAnswer at once with 202 Accepted and status queued, and run the capture when a browser is free; read it back with GET /captures/{id} or receive it at callback_url. json and cloud_storage only - with by_format it is a validation failure.
timeoutinteger120Async captures only: the seconds of browser time the capture may use, every attempt included, 30-300. Queue time does not count. Sent without async it is a validation failure.
callback_urlstring—An https URL the finished capture is posted to: the capture.succeeded or capture.failed webhook event, and delivery.completed when a cloud_storage upload has finished or finally failed, signed with your account's secret and retried like any webhook delivery. A request refused before the capture ran, with the refusal in its own response (capture_busy, service_unavailable, queue_full), sends nothing. Needs a plan that includes webhooks; otherwise 403 feature_not_available.

Media and format

FieldTypeDefaultDescription
media_categorystringimageimage, video, pdf or content. Videos are charged against the video allowance; everything else, page content included, against the image allowance.
formatstringresolved from media_categoryThe produced file format. It must be consistent with media_category — asking for mp4 as an image is a validation failure, not a silent correction.

An omitted format resolves from the category rather than to one fixed value: image gives png, video gives mp4, pdf gives image_pdf, content gives markdown.

Allowed formats for an image capture:

pngjpegwebptiff

For a video capture:

mp4webmmovgif

A video records the page scrolling from the top and stops at the bottom of the page or after 30 seconds, whichever comes first. A lower scrolling_delay_ms or a smaller zoom fits more of a long page in.

For a PDF capture:

image_pdfdocument_pdf

For a page-content capture (see Page content):

markdowntext

Two kinds of PDF

image_pdf is image-based: the page is rendered and the picture placed into a document, so it looks exactly like the screenshot. document_pdf is written by the browser itself, so its text stays selectable and searchable. A PDF is only ever asked for under media_category: pdf. The older spelling pdf still means image_pdf and is accepted indefinitely; responses spell it image_pdf.

document_pdf captures the visible page only

The browser writes one document per screen height and we do not join documents, so document_pdf with scroll_mode: scroll is refused rather than delivered as its first page. For a scrolling capture use format: image_pdf, whose pages are images and are merged into one document.

Page content

Set media_category to content (or just send "format": "markdown") to get the page's readable main content instead of a picture: the article, docs or product text without navigation, footers, ads, cookie banners or scripts. markdown keeps headings, lists, links and tables; text is the same content without markup.

Read a page as Markdown
{
  "url": "https://example.com/blog/post",
  "media_category": "content",
  "format": "markdown",
  "response_type": "by_format"
}

The page is loaded in a real browser, exactly like a screenshot, so pages built with JavaScript work, and proxy, headers and cookies apply. A content capture counts as one capture against the same allowance as an image and is charged only when it succeeds.

With response_type: by_format the answer is the text itself (text/markdown; charset=utf-8 or text/plain; charset=utf-8); with json you get a download_url as usual.

At most 2 MB of text

The text is at most 2 MB. A longer page fails with payload_too_large and is not charged. The limit is published as limits.content_max_bytes in the defaults.

What a content capture takes

scroll_mode: scroll and selectors.click work, for pages that load more as they are read or hide their text behind a consent or “show more” button. page_source, timestamp_watermark, auto_height, max_height, selectors.xpath, selectors.hide and selectors.hover are not available for content captures; sending one is a validation failure.

Page source

Send a page_source block to save the page's own source alongside the capture — one run, two files. Sending the block is what turns it on; there is nothing else to set.

FieldTypeDefaultDescription
page_source.formatstringhtmlThe form the source is saved in: html for the page's markup, text for its rendered words without markup, json for the JSON body an API endpoint served.
htmljsontext

json only when the page really is JSON

json is for capturing an API endpoint alongside a picture of it: the response body is validated as JSON before it is stored. A page asked for as json that turns out to be an ordinary web page is saved as html and says so in page_source.format, rather than handing you markup under application/json.

The source comes back as its own page_source block on the response, with adownload_url of its own that expires when the capture's does. Under response_type: cloud_storage it is pushed to your bucket as a second object beside the capture.

It counts as a second capture

The page source is charged against your image allowance, whatever it rides with — so one request for a screenshot plus its source spends two units. A request that would exceed your allowance is refused before anything is captured.

Not available with by_format

response_type: by_format answers with the raw file as the response body, and one body cannot carry two files. Sending page_source with it is a validation failure — use json or cloud_storage.

Device and viewport

FieldTypeDefaultDescription
device_categorystringdesktopdesktop or mobile.
emulated_devicestringthe seeded default deviceThe name of a device descriptor, e.g. iPhone 13, matched case-insensitively against emulated_devices from GET /catalog. Only meaningful with device_category: mobile — a mobile request that names no device is given the platform default rather than a desktop viewport.
viewport.widthinteger (px)1920Browser window width.
viewport.heightinteger (px)1080Browser window height.
viewport.device_scale_factordecimal1Pixel density, between 1 and 3. A value of 2 doubles the resolution of the produced image — and roughly quadruples its file size.
zoominteger (%)100Page zoom, 50 to 100. Applied to a desktop video only (media_category video without an emulated device), where it changes how much fits on screen. Any other value than 100 on another capture is refused.

A mobile device overrides the viewport

When emulated_device resolves to a real device, that descriptor decides the viewport, the pixel density and the touch behaviour. A viewport block sent alongside it is ignored rather than rejected.

What gets captured

FieldTypeDefaultDescription
scroll_modestringviewportviewport captures what fits on screen; scroll walks the whole page. A video defaults to scroll instead, because it needs movement to be worth recording.
auto_heightbooleanfalseGrow the capture to the page’s own height rather than stopping at the viewport height.
max_heightinteger (px)100000 when auto_height is on (16383 for webp, 65500 for jpeg)The ceiling auto_height may grow to, up to 100000. Without a bound, one pathological page could grow without limit, so a default is always applied. WebP cannot exceed 16383 pixels and JPEG 65500, so for those formats the default is the limit divided by the device scale factor (webp: 8191 at 2, 5461 at 3) and a larger value is a validation failure.
ValueMeaning
viewportOne screenful. Fastest, and the right default for a thumbnail or a preview.
scrollThe renderer scrolls the page, pausing scrolling_delay_ms at each step so lazy-loaded content has time to appear.

Blocking and timing

FieldTypeDefaultDescription
block_adsbooleantrueBlock advertising. On by default, because ads are one of the main causes of an unusable capture.
block_pop_upsbooleantrueDismiss pop-ups: answers cookie banners, hides known sign-up pop-ups, chat widgets and app banners, and closes other pop-ups with their own close button. On by default, for the same reason.
press_escapebooleanfalsePress the Escape key after the pop-up pass and again after scrolling (once in a video), for a modal that closes on Escape. Works with or without block_pop_ups.
page_timeout_msinteger (ms)30000How long the page is given to load. Accepts 1000–60000. A page that never settles answers 504 capture_timeout.
scrolling_delay_msinteger (ms)500How long to pause after each scroll step (half a window) so content that loads as it comes into view has time to appear. 0–10000; raise it for a page whose sections fill in slowly, at the cost of a longer capture. Ignored when scroll_mode is viewport.

Set your client timeout above page_timeout_ms

The call is synchronous. A full-page capture of a long site can legitimately take most of the allowed time, so an HTTP client timeout of, say, 10 seconds will abandon captures that were about to succeed. Every answer leaves within 2 minutes; a client timeout of 130 seconds is always enough.

Browser identity

FieldTypeDefaultDescription
user_agentstring—Overrides the browser’s own user agent. Omit it to leave the value alone.
localestring—A BCP-47 language tag, e.g. en-GB. Changes the Accept-Language header and the browser’s reported language.
time_zonestring—An IANA time zone id, e.g. Europe/Berlin. Changes what the page’s own date and time code reports.
headersobject—Extra request headers as a flat name/value object, sent with the requests to the captured site and its subdomains, never to the other sites the page loads from. A few hop-by-hop headers (Host, Content-Length and similar) are ignored.
cookiesobject—Cookies to present, as a flat name/value object. They are set for the origin of url.

Omitted is not empty

These five fields, plus geolocation, are reported by the defaults endpoint under left_to_the_browser. Leaving one out means the browser’s own value stands — which is not at all the same as sending an empty one.

selectors

What to capture, and what to do to the page before the shot. Every field is optional and read by presence: an empty or whitespace value means “no selector”, never “an empty selector”. Each may be at most 512 characters, except hide, which takes up to 25 selectors in 2000 characters.

FieldTypeDefaultDescription
selectors.xpathstring—Capture only this element instead of the whole page, e.g. //main.
selectors.hidestring (CSS)—Elements to hide before the shot — cookie banners, chat widgets, sticky headers. Up to 25 comma-separated CSS selectors (2000 characters); an XPath starting with // works too. Each is applied on its own, so one the browser rejects is skipped rather than failing the capture, and elements the page adds later are hidden too.
selectors.clickstring (CSS)—One element to click first, typically a consent button.
selectors.hoverstring (CSS)—One element to hover first, e.g. to open a menu you want captured open.

Prefer hide over click for banners

Clicking depends on the button existing and on the click taking effect before the shot. Hiding the banner works whether or not it is interactive, and does not risk setting a consent cookie you did not intend to set.

geolocation

FieldTypeDefaultDescription
geolocation.latitudedecimal—Between -90 and 90.
geolocation.longitudedecimal—Between -180 and 180.
geolocation.accuracydecimal (metres)100How precise the reported position is, 1 to 10000 metres. Only meaningful with latitude and longitude; a page that asks for the position sees this as the accuracy radius.

Omitting the block leaves the browser’s own behaviour untouched — it does not report a position of 0,0. This sets what the page is told when it asks the browser where it is; it does not change where the request comes from. For that, use proxy.

proxy

FieldTypeDefaultDescription
proxy.countrystring—The proxy location, matched case-insensitively against proxy_countries from GET /catalog — the countries the platform can capture from right now. Required whenever the block is present.

Only the location is named. Which proxy is used and what its credentials are belong to the platform and never travel over this API.

timestamp_watermark

A capture carries no timestamp strip unless the request sends this block. Sending the block at all is what turns one on, so "timestamp_watermark": {} is already a complete instruction.

Three ways to send it
// A complete instruction: the block being present turns the strip on
{ "timestamp_watermark": {} }

// Same strip, moved and recoloured
{
  "timestamp_watermark": {
    "location": "bottom",
    "font_color": "#FFFFFF",
    "background_color": "#111111"
  }
}

// Colours kept, strip switched off
{ "timestamp_watermark": { "enabled": false } }
FieldTypeDefaultDescription
timestamp_watermark.enabledbooleantrue when the block is sentSending false keeps the colours and location described here while producing no strip — which is how a stored request can be switched off without being rewritten.
timestamp_watermark.locationstringtoptop or bottom. A location you send and get wrong is refused, not guessed.
timestamp_watermark.font_colorstring (hex)#000000With or without the leading #.
timestamp_watermark.background_colorstring (hex)#FFFFFFThe strip colour. With or without the leading #.

s3_delivery

Required for response_type: cloud_storage and rejected for the other two modes. The fields, the credential handling and the failure modes are covered in full on Deliver to your S3 bucket.

Plan-gated fields

A few fields are accepted by the schema but refused by your plan. They answer 403 feature_not_available with the field named, rather than being silently dropped:

  • headers and cookies — every paid plan, not the free trial
  • proxy — every paid plan, not the free trial
  • callback_url — every paid plan, not the free trial

Which plan includes which is set out on the pricing page. A refusal here is a plan question, not a request bug — the same body will work unchanged after an upgrade.

Did this page answer your question?

Tell us what is missing