Web Screenshot API — Capture Any Page as PNG or JPEG
Documentation menu
FetchLayer FetchLayer API

Screenshot a web page

Capture any public web page as a PNG or JPEG, either the visible area or the whole scrollable page. An image is never a simple page, so a screenshot starts at the second tier rather than the first — everything above that is costed the same as a fetch. A page is always served the cheapest way that works, and costs more only when an easier attempt has genuinely failed — so an easy page never subsidises a hard one. `pagesFetched` and `usage.billableUnits` report what this particular request actually cost, in units, at three units to the credit: **1 for a simple page, 3 for a harder one, 6 for a hard one, 9 for the hardest**. The whole request runs inside a 120-second budget. Work that cannot finish in what is left of that budget is **skipped and reported as skipped**, never started and cut off halfway — which would bill for work that produced nothing. A 404 returns immediately: the page is not there, and working harder cannot change that. A 503 means the page could not be read at all, and it is not billed. The hardest pages are also the slowest. In a large burst some will exhaust the 120-second budget and come back unread rather than wrong, so expect a slow tail and retry it rather than sizing every timeout for the worst case. The image comes back base64-encoded in `imageBase64`, with its format, byte size and captured dimensions alongside. jpeg is much smaller than png on a photographic page. Billing: a screenshot starts at 3 units — one credit — rising to 6 on a hard page and 9 on the hardest. The cost is on every response as `pagesFetched`, and explained in `usage.billing`. A 503 is not billed.

POST /screenshot Pages
https://api.fetchlayer.dev/web-unblocker/screenshot
OpenAPI JSON

Headers

Authorization string required

Bearer token — your FetchLayer API key.

Parameters

url string required

The page to fetch. Must be an http or https address.

render string optional

Whether to run the page in a browser: auto, always, never. auto only runs one when a direct read is not enough. (default: auto)

maxEffort string optional

Your ceiling on how hard this request may try, cheapest first: basic, standard, advanced, maximum. It sets a ceiling on cost rather than choosing a tier — the cheapest that works is always used regardless. Set it to plain when you would rather a page failed than cost more than the cheapest tier. (default: solve)

country string optional

Two-letter country code to read the page from, when the page differs by country.

session string optional

An arbitrary label. Requests sharing a label are read over the same connection where possible, so a multi-step flow looks like one visitor.

assets string optional

How much of the page to load: minimal, standard, full. minimal is the document only; standard loads what is needed to render, without images or video; full loads everything. Lower settings are faster and cheaper. (default: standard)

waitUntil string optional

When to consider the page ready: domcontentloaded, load, networkidle. Only applies when the page runs in a browser. (default: domcontentloaded)

waitForSelector string optional

A CSS selector to wait for before reading the page. If it never appears the request is reported unsuccessful rather than returning a partial page.

waitMs integer optional

Extra time to let the page settle, in milliseconds, up to 15000.

expectText string optional

Text that must appear on the page. If it does not, the request is reported unsuccessful rather than handing back the wrong page — a block page instead of the article you asked for.

device string optional

Read the page as a desktop or mobile visitor. (default: desktop)

headers object optional

Request headers to send to the page, such as Accept-Language or Referer. Headers that could impersonate another caller are not forwarded, and any that are dropped are listed in notes.

maxRedirects integer optional

Redirects to follow before giving up, up to 20. Every hop is re-validated against the same address rules as the first.

format string optional

Envelope format: json or markdown. (default: json)

fullPage boolean optional

Capture the whole scrollable page rather than just the visible area. (default: false)

imageFormat string optional

png or jpeg. jpeg is much smaller for a photographic page. (default: png)

Response Fields

screenshot

Screenshot a web page

transport string

The source. Always "web".

notes string[]

Caveats you can act on: a page that carried no readable text, a body cut short at maxBytes, request headers that were not forwarded, or an escalation ceiling that was applied.

pagesFetched number

What this request cost, in units — the same number as usage.billableUnits, under the field name billing reads. It is a cost, not a page count. A unit is a third of a credit, and the figure reflects how hard the page was to get: a simple page is 1 unit, a harder one 3 (one credit), a hard one 6, and the hardest 9 (three credits). A 503 is not billed.

url string

The address that was requested.

finalUrl string

The address the request ended at, after redirects.

status number

The HTTP status the page answered with.

redirects object[]

Redirects that were followed, in order.

title string

The page title, when it has one.

imageBase64 string

The image, base64-encoded.

imageFormat string

png or jpeg.

imageBytes number

Size of the image in bytes.

width number

Captured width in pixels.

height number

Captured height in pixels.

fullPage boolean

Whether the whole scrollable page was captured.

usage object

What the request cost, how long it took, and how much came back.

usage.bytesTransferred number

Bytes transferred across every attempt. A lower bound when a browser was used, because some responses do not declare their length.

usage.totalMs number

Wall-clock time for the whole request, every attempt included.

usage.step string

The effort level that served the page — basic, standard, advanced or maximum, the same vocabulary as the maxEffort ceiling. This is what the charge is derived from, so a request that cost more than expected says which tier it landed in.

usage.billableUnits number

What this request cost, in units, at three units to the credit. The same number as pagesFetched. Charged requests run on a per-account counter rather than a fraction taken off each response, so one response shows the units and the credit falls on every third one — it averages to a third of a credit per unit over any real volume.

Request

curl -X POST "https://api.fetchlayer.dev/web-unblocker/screenshot" \
  -H "Authorization: Bearer ss-your-key" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/","imageFormat":"jpeg"}'

Response

{
  "transport": "web",
  "notes": [
    "This page was run in a browser and still returned no readable text."
  ],
  "pagesFetched": 2,
  "url": "https://example.com/",
  "finalUrl": "https://example.com/",
  "status": 200,
  "redirects": [],
  "title": "Example Domain",
  "imageBase64": "/9j/4AAQSkZJRgABAQAAAQ…",
  "imageFormat": "jpeg",
  "imageBytes": 31462,
  "width": 1280,
  "height": 720,
  "fullPage": false,
  "usage": {
    "step": "standard",
    "bytesTransferred": 671,
    "totalMs": 7483,
    "billableUnits": 3
  }
}