Observation & data

Capture a page once it has finished painting

A single PNG or JPEG of a URL — full page, viewport, or one CSS selector — captured only after webfonts resolve, images decode, entrance animations play out and two frames come back byte-identical.

screenshot

What it is

The job: Get one honest image of a page, without catching it mid fade-in.

The naive screenshot fires at the load event, which on a modern page is well before the page looks finished: the hero is still fading in, the webfont has not swapped, the images have not decoded. The result is a picture of a page nobody ever sees. This capture waits for visual completion instead — load, a bounded network idle, fonts resolved, images decoded, finite CSS and Web animations played out, and two byte-identical probe frames, which also catches requestAnimationFrame and canvas work that the platform reports nothing about.

You can scope the capture: `fullPage` for the whole scrollable document, a `selector` for exactly one element, or neither for the viewport. Encoding is PNG by default; pass type "jpeg" with a quality for a far smaller payload, which is the right pick when the image is going into a vision model rather than onto a screen.

No action is taken, no goal is run, and no model is called. Over MCP the image comes back as base64 with its mime type and byte length; the REST endpoint returns the image bytes directly.

The call

Call it exactly like this.

Copied from the tool's registration and the route handler — not paraphrased.

MCPscreenshot.mcp.jsonjson
// MCP tool call — server "twin-browser"
{
  "tool": "screenshot",
  "arguments": {
    "url": "https://example.com",
    "fullPage": true,
    "type": "jpeg",
    "quality": 70
  }
}
POST /api/v1/screenshotrequest.shbash
curl -X POST https://twin-browser.com/api/v1/screenshot \
  -H "Authorization: Bearer $TWIN_API_KEY" \
  -H "content-type: application/json" \
  -d '{"url":"https://example.com","fullPage":true,"type":"jpeg","quality":70}'
Parameters accepted by screenshot
ParameterTypeWhat it does
url*stringThe URL to capture. Its origin must be a well-formed http(s) target.
fullPagebooleanCapture the full scrollable page. Default false — viewport only.
selectorstringCSS selector to capture just one element instead of the page.
type"png" | "jpeg"Encoding. png (default) is lossless; jpeg is lossy and far smaller.
qualitynumberJPEG quality 1–100, default 70. Ignored for png.
settlebooleanWait for the page to be visually finished before capturing. Default true; false captures at the load event.
settleMsnumberCeiling in milliseconds on the readiness wait (default 8000). A quiet page never reaches it; this only bounds a page that never stops moving.
animations"disabled" | "allow"disabled (default) fast-forwards finite animations to their end state and freezes infinite ones at capture time. allow films whatever frame is live.
waitMsnumberExtra wait in milliseconds applied AFTER the readiness settle, for something only you know about.
proxystringYour own egress proxy URL.
blockAssetsbooleanAbort image/media/font requests. Default OFF for screenshots, so visuals are kept.

Returns

response.jsonjson
// MCP
{ "image": "<base64>", "mime": "image/jpeg", "bytes": 184203,
  "fullPage": true, "selector": null, "url": "https://example.com", "status": 200,
  "settle": { /* what the readiness gate observed */ } }

// REST: the image bytes (image/png or image/jpeg).
// The charge is in the x-credits-charged response header.

What it costs

1 credit

Flat, and there is no LLM cost to meter — a screenshot takes no action and runs no model. The charge is reported in the x-credits-charged header on the REST response.

See the full rate card

Which one

When a different tool is the right call.

The honest answer is often the neighbouring tool. These are the trades.

observe_page

If the next thing you do is click something, take the element map, not the picture: an index is actionable and a pixel is not. Screenshot wins when a vision model is doing the reading, or when a human has to look at it.

observe_page
record_run

One frame versus the whole session. record_run captures a video of a run being performed; screenshot captures a page as it stands, with no run at all.

record_run

Questions

screenshot, answered.

Why is my capture slower than a plain Playwright screenshot?
Because it waits for the page to stop moving. The readiness gate resolves webfonts, decodes images, plays out finite animations and requires two byte-identical probe frames before capturing. A quiet page settles well under the ceiling; a page with a permanent animation hits settleMs and is captured anyway. Pass settle: false to get the old capture-at-load behaviour.
PNG or JPEG for feeding a vision model?
JPEG at quality 70. The payload is a fraction of the PNG and the detail loss is invisible to a model reading layout and text. Keep PNG when a human is going to inspect fine detail, or when you are diffing captures byte-for-byte.