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.
// MCP tool call — server "twin-browser"
{
"tool": "observe_page",
"arguments": {
"url": "https://example.com/pricing",
"waitMs": 800
}
}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"}'| Parameter | Type | What it does |
|---|---|---|
| url* | string | The URL to map. Its origin must be a well-formed http(s) target; a malformed URL is a 400. |
| waitMs | number | Settle wait in milliseconds after load, before serializing — for pages that hydrate late. |
| proxy | string | Your own egress proxy URL. |
| blockAssets | boolean | Abort image/media/font requests. Defaults ON when a proxy is set; harmless for a map-only read. |
Returns
{
"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 cardWhich one
When a different tool is the right call.
The honest answer is often the neighbouring tool. These are the trades.
screenshotBoth 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.
screenshotextractextract 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.
extractmap_siteSimilar name, different scale: map_site enumerates a site’s URLs, observe_page maps one page’s elements.
map_siteQuestions
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.