Observation & data

Serialize a page into an indexed element map

The read-only primitive: navigate, serialize the DOM into a compact indexed element map plus navigation timing, take no action and run no model.

observe_page

What it is

The job: Let an agent see what is on a page before deciding what to do about it.

A raw HTML page is the wrong input for a language model: it is enormous, mostly irrelevant, and full of markup that carries no decision-relevant signal. observe_page returns the indexed element map instead — the interactive elements the agent could act on, each with a stable index, plus the page URL, HTTP status and navigation timing (nav, DOMContentLoaded, load).

This is the same serialization the planner uses on every step of a run, exposed on its own. Nothing is clicked, nothing is typed, and no model is invoked — which is why it costs a flat credit and never carries an LLM bill.

Asset blocking is the sensible default here: a map-only observation does not need images or fonts, and skipping them is faster and cheaper. It is on automatically when a proxy is set, and you can force it either way.

The call

Call it exactly like this.

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

MCPobserve_page.mcp.jsonjson
// MCP tool call — server "twin-browser"
{
  "tool": "observe_page",
  "arguments": {
    "url": "https://example.com/pricing",
    "waitMs": 800
  }
}
POST /api/v1/observerequest.shbash
curl -X POST https://twin-browser.com/api/v1/observe \
  -H "Authorization: Bearer $TWIN_API_KEY" \
  -H "content-type: application/json" \
  -d '{"url":"https://example.com/pricing"}'
Parameters accepted by observe_page
ParameterTypeWhat it does
url*stringThe URL to map. Its origin must be a well-formed http(s) target; a malformed URL is a 400.
waitMsnumberSettle wait in milliseconds after load, before serializing — for pages that hydrate late.
proxystringYour own egress proxy URL.
blockAssetsbooleanAbort image/media/font requests. Defaults ON when a proxy is set; harmless for a map-only read.

Returns

response.jsonjson
{
  "elements": [ /* the indexed, interactive element map */ ],
  "url": "https://example.com/pricing",
  "status": 200,
  "timing": { "navMs": 812, "domContentLoadedMs": 640, "loadMs": 1104 }
}

What it costs

1 credit

Flat. No action is taken and no model is called, so there is no metered cost to settle against — the 1 credit is browser time and nothing else.

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.

screenshot

Both read a page without acting. observe_page gives structure an agent can act on by index; screenshot gives pixels a vision model can look at. If your next step is a click, you want the map, not the picture.

screenshot
extract

extract runs a model over the page and returns the fields you asked for. observe_page returns the raw map and leaves the reasoning to you — cheaper, and the right call when you want to plan an action rather than pull data.

extract
map_site

Similar name, different scale: map_site enumerates a site’s URLs, observe_page maps one page’s elements.

map_site

Questions

observe_page, answered.

Why not just fetch the HTML myself?
Because the page you would fetch is not the page a user sees. observe_page runs a real browser: JavaScript executes, the DOM hydrates, and the serialization happens after that. It also indexes the elements, which is what makes the output actionable — an agent refers to element 12, not to a CSS selector it guessed.
Does the element index stay stable between calls?
Within one page state, yes — that is what makes it usable as an action target. Across a navigation or a re-render the page has genuinely changed, so re-observe rather than reusing an old index.