# Screenshots for AI agents: let a model see a page

> When to give an AI agent a screenshot, HTML, markdown or the accessibility tree, what a screenshot costs in tokens on each vendor, and how to wire it up.

Explainer by Serghei Pogor, founder of CurlShot (https://curlshot.com/about/serghei-pogor). Published 17 October 2026. Page for people: https://curlshot.com/blog/screenshots-for-ai-agents

Give a model a screenshot when the question is about how a page looks, and text or markdown when it is about what the page says. Send one viewport, not the whole page: a 390 × 844 capture is 434 visual tokens on Claude and a 1280 × 800 capture is 1,334, while a 1280 × 6000 full-page capture is shrunk to 335 × 1568 on the standard Claude tier or billed 9,024 tokens at OpenAI's default detail on gpt-6-astra. Connect the screenshot tool over plain HTTP or MCP, and treat everything the model reads on the page as untrusted data.

## Key facts

- Claude counts one visual token per 28 × 28 px patch: tokens = ⌈width / 28⌉ × ⌈height / 28⌉. Images are downscaled above 1568 px or 1568 tokens (standard tier), or 2576 px and 4784 tokens (Claude 4.7 and later). Checked 11 October 2026.
- The newer OpenAI models count 32 × 32 px patches times a 1.2 multiplier, so 1280 × 800 is 1,000 patches and 1,200 tokens. gpt-4o and gpt-4.1 use tiles: 85 + 170 per 512 px tile.
- Gemini 3 charges by media_resolution, not by pixel size: 280, 560, 1120 or 2240 tokens per image, 1120 by default.
- A 1280 × 1024 capture is 1,702 Claude tokens, just over the standard tier limit, so it is downscaled to 1225 × 980. A 1280 × 800 capture is not.
- The image format changes the upload size, not the token count. Tokens are computed from pixel dimensions.
- The CurlShot MCP server is one endpoint (/api/mcp, streamable HTTP) with two tools: take_screenshot and list_devices.

## An agent looks at a web page: the conversation, step by step

An illustrative example, not a recording. The file size and the quota figure are invented; the token counts are computed.

1. **User:** Our pricing page is at https://example.com/pricing. On a phone, can people see the "Start free" button without scrolling?
   - Why a screenshot and not the HTML: Whether a button lands on the first screen depends on fonts, images, spacing and whatever sits above it. The HTML source lists the button but not where it ends up. Only a rendered page answers a question about layout.
2. **Agent:** I will look at the page the way a phone shows it.
   - The model decides to look: The agent has a screenshot tool in its tool list. It reads the tool description and chooses the arguments itself: nobody wrote this call by hand.
3. **Tool call:** `{ "name": "take_screenshot", "arguments": { "url": "https://example.com/pricing", "viewport_width": 390, "viewport_height": 844 } }`
   - What size was requested: A 390 by 844 window and only the first screen, because full_page is off unless asked for. Through the CurlShot MCP server the format defaults to jpeg and ads and cookie banners are blocked, so no banner covers the button.
4. **Tool result:** Captured https://example.com/pricing, 390 x 844 px, 41 KB, image/jpeg, 86 screenshots left this period. Download: https://curlshot.com/api/v1/files/… (valid for about a day)
   - What it cost: One screenshot of the account quota. For the model, the image is 14 × 31 = 434 visual tokens on Claude, 422 tokens on the OpenAI patch models, and about 1,120 on Gemini 3 at its default resolution. The file size (41 KB here, an invented figure) does not change the token count: pixels do.
5. **Agent:** Yes. On a 390 by 844 screen the "Start free" button sits under the first plan and is fully on the first screen. The second plan starts below the fold.
   - The answer comes from pixels: The model read the image and answered from what it saw. Anything it read on that page is data from a stranger, not an instruction: see the safety note further down.

## Screenshot, HTML, markdown or accessibility tree: what should a model get?

| Way | What the model gets | What it misses | Relative size | Best for |
| --- | --- | --- | --- | --- |
| Screenshot | The page as a person sees it: layout, colour, images, charts, what is cut off or overlapping. | Link addresses, hidden text, anything below the captured area. Small text can be misread. | Fixed by pixel size, not by how much text is on the page. About 400 to 1,700 tokens for one viewport (table below). | Questions about how a page looks: visual checks, design review, charts, "is this broken?". |
| Raw HTML | Every element, attribute, link address and inline script, exactly as served. | What it looks like. Content drawn later by JavaScript is absent unless the HTML is taken after rendering. | The largest. Markup, scripts and styles ride along with the words, and it grows with the page. | Finding selectors, reading attributes and structured data, debugging markup. |
| Extracted text or markdown | The words, headings, lists, tables and links, with the markup stripped. | Layout, images, charts, colour. Extraction can drop or reorder content on complex pages. | The smallest for text: roughly the words on the page. | Reading: articles, documentation, long pages, anything you want quoted exactly. |
| Accessibility tree | One entry per element with its role, name, description and state: "button, Start free". | Appearance and position. It is only as good as the semantics of the page. | In between: smaller than the HTML, larger than plain text. | Acting on a page: deciding what can be clicked, typed into or toggled. |

Pick by the question:

- "Does it look right?" Screenshot. Only pixels show overlap, clipping, contrast and broken images.
- "What does it say?" Text or markdown. Exact words at the lowest cost. A screenshot of a long article is the wrong choice.
- "What can I do here?" Accessibility tree. Roles and names tell an agent what is a button, a field or a link.
- "Why is it built this way?" Raw HTML. Attributes, selectors and embedded data are only in the source.

CurlShot makes the screenshot. It does not extract text or an accessibility tree from a page.

## How many tokens does a screenshot cost?

What each vendor's documentation says, read on 11 October 2026. These rules differ by model and change often: check the linked page before you budget.

### Anthropic (Claude)

Claude views an image in 28 × 28 pixel patches, and each patch is one visual token.

Formula: `tokens = ⌈width / 28⌉ × ⌈height / 28⌉`

- High-resolution tier, "Claude 4.7 and later models": long edge up to 2576 px and up to 4784 visual tokens.
- Standard tier, "All other models": long edge up to 1568 px and up to 1568 visual tokens.
- A larger image is scaled down to the largest size that fits both limits, keeping its aspect ratio. The documentation gives a reference implementation, used for the table below.
- The token limit usually bites first: a 1920 × 1080 screenshot becomes 1456 × 819 on the standard tier, not 1568 × 882.
- Hard limit: 8000 × 8000 px per image. A request with more than 20 images gets a stricter per-image limit, and Anthropic advises keeping each side at 2000 px or less there.

### OpenAI (GPT models)

Newer models count 32 × 32 pixel patches and multiply by a model factor. Older models count 512 px tiles on top of a base cost.

Formula: `patch models: tokens = ⌈⌈width / 32⌉ × ⌈height / 32⌉ × multiplier⌉ · tile models: tokens = base + tiles × tile tokens`

- The multiplier is 1.2 for gpt-6-astra, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4 and gpt-5.2, and 1.62 for gpt-4.1-mini.
- The detail parameter (low, high, original or auto) sets the resizing. For gpt-6-astra, high scales the image down to a 2,500-patch budget, and auto, the default, behaves like original: no downscaling.
- An image that still needs more than 30,000 patches is rejected, not resized.
- Tile models: gpt-4o and gpt-4.1 cost 85 base tokens plus 170 per tile, gpt-5.1 costs 70 plus 140. The image is first fitted into 2048 × 2048, then its shortest side is scaled to 768 px.
- With detail: low a tile model charges only its base tokens, whatever the size.

### Google (Gemini)

On Gemini 3 models the token count of an image is set by the media_resolution level, not by its pixel size.

Formula: `Gemini 3: low 280 · medium 560 · high 1120 · ultra_high 2240 · unspecified (default) 1120`

- The documentation calls these "approximate token counts" and the "maximum number of tokens allocated per input image".
- It recommends high (1120) for most image analysis, and names ultra_high (2240) for computer use.
- The image understanding page also gives a tile rule without naming a model: 258 tokens when both sides are 384 px or less, otherwise 768 × 768 tiles at 258 tokens each.
- Google describes its tile count formula as "rough" and does not state the rounding, so the table below uses only the Gemini 3 media_resolution figure.

### Worked examples for five capture sizes

| Capture | Claude, standard tier | Claude, high-resolution tier | OpenAI patches, detail high | OpenAI patches, detail auto | OpenAI tiles, detail high | Gemini 3, default resolution |
| --- | --- | --- | --- | --- | --- | --- |
| Phone viewport, 390 × 844 | 434 (14 × 31) | 434 (14 × 31) | 422 (13 × 27 = 351, × 1.2) | 422 (13 × 27 = 351, × 1.2) | 425 (85 + 2 × 170) | 1,120 |
| Small desktop viewport, 1024 × 768 | 1,036 (37 × 28) | 1,036 (37 × 28) | 922 (32 × 24 = 768, × 1.2) | 922 (32 × 24 = 768, × 1.2) | 765 (85 + 4 × 170) | 1,120 |
| Laptop viewport, 1280 × 800 | 1,334 (46 × 29) | 1,334 (46 × 29) | 1,200 (40 × 25 = 1,000, × 1.2) | 1,200 (40 × 25 = 1,000, × 1.2) | 1,105 (85 + 6 × 170), resized to 1228 × 768 | 1,120 |
| Default viewport of the API, 1280 × 1024 | 1,540 (44 × 35), resized to 1225 × 980 | 1,702 (46 × 37) | 1,536 (40 × 32 = 1,280, × 1.2) | 1,536 (40 × 32 = 1,280, × 1.2) | 765 (85 + 4 × 170), resized to 960 × 768 | 1,120 |
| Full page, 6,000 px tall, 1280 × 6000 | 672 (12 × 56), resized to 335 × 1568 | 1,840 (20 × 92), resized to 550 × 2576 | 2,981 (23 × 108 = 2,484, × 1.2), resized to 736 × 3449 | 9,024 (40 × 188 = 7,520, × 1.2) | 765 (85 + 4 × 170), resized to 436 × 2048 | 1,120 |

Which models each column covers:

- Claude, standard tier: all other Claude models.
- Claude, high-resolution tier: Claude 4.7 and later models.
- OpenAI patches, detail high: gpt-6-astra with detail: high.
- OpenAI patches, detail auto: gpt-6-astra with detail: auto or original (auto is the default).
- OpenAI tiles, detail high: gpt-4o and gpt-4.1 with detail: high or auto.
- Gemini 3, default resolution: Gemini 3 models, media_resolution unspecified.

Anthropic's own price example: at Claude Haiku 4.5's $1 per million input tokens, a 1000 × 1000 image (1,296 tokens) costs about $1.30 per thousand images. By the same arithmetic the 390 × 844 capture is 434 × $1 / 1,000,000 = $0.000434, or about $0.43 per thousand.

## What size screenshot should you send to an LLM?

1. **Capture the viewport, not the whole page.** A 1280 × 6000 full-page capture is scaled down to 335 × 1568 on the Claude standard tier, about a quarter of its width, and body text stops being readable. At OpenAI's default detail on gpt-6-astra the same image costs 9,024 tokens. Options: `Leave full_page off. It is false by default.`.
2. **Stay inside the size the model sees natively.** A 1280 × 800 capture is 1,334 visual tokens and passes through the Claude standard tier untouched. The 1280 × 1024 default is 1,702 and gets downscaled to 1225 × 980. If the provider resizes for you, you lose sharpness you paid to render. Options: `viewport_width, viewport_height`.
3. **Block cookie banners and ads.** A consent banner covers the content the question is about, and an ad is text the model may read as part of the page. Both cost the same tokens as real content. Options: `block_cookie_banners=true, block_ads=true (on by default through MCP, off by default in the HTTP API)`.
4. **Use a phone viewport for dense reading.** At 390 px wide a responsive page reflows into one column, so text stays large relative to the image, and the capture is only 434 Claude tokens. Set the size yourself: a device preset such as iphone_14 renders at 3 device pixels per CSS pixel, which makes a 1170 × 2532 image. Options: `viewport_width=390&viewport_height=844, or a viewport_device with device_scale_factor=1`.
5. **Crop to the element in question.** A screenshot of only the pricing table is smaller and removes everything the model could be distracted by. The image is exactly as big as the element. Options: `selector, or clip_x, clip_y, clip_width, clip_height`.
6. **Pick the format for legible text.** Tokens are counted from pixel dimensions, so the format changes the upload size, not the token cost. PNG is lossless. JPEG and WebP are much smaller, but heavy compression blurs small text, which Anthropic warns can hurt model performance. Options: `format=png, or format=jpeg / webp with image_quality (1 to 100, default 80)`.
7. **Cache repeat looks.** Agents often look at the same page several times in one task. A stored copy comes back faster and does not use a screenshot of the quota. Leave it off when the agent is checking a change it just made. Options: `cache=true (a copy is kept for 4 hours unless cache_ttl says otherwise)`.

## How to wire a screenshot tool into an agent

### 1. A plain HTTP request

```bash
curl "https://curlshot.com/api/v1/screenshot?url=https://example.com&viewport_width=1024&viewport_height=768&format=jpeg&block_ads=true&block_cookie_banners=true" \
  -H "X-Access-Key: YOUR_ACCESS_KEY" \
  --output page.jpg

curl "https://curlshot.com/api/v1/screenshot?url=https://example.com&selector=h1&format=png" \
  -H "X-Access-Key: YOUR_ACCESS_KEY" \
  --output heading.png
```

The answer is the image file. Send it to the model as an image part of your next message.

### 2. The MCP server

Endpoint: `https://curlshot.com/api/mcp`, streamable HTTP, JSON-RPC 2.0 over POST, no session. Client configuration:

```json
{
  "mcpServers": {
    "screenshots": {
      "type": "http",
      "url": "https://curlshot.com/api/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_ACCESS_KEY"
      }
    }
  }
}
```

| Tool | Needs a key | What it does | Arguments |
| --- | --- | --- | --- |
| `take_screenshot` | Yes | Renders a public web page and returns it as an image part (base64) plus a one-line text summary with a download link. | url (required), format, full_page, viewport_device, viewport_width, viewport_height, dark_mode, block_ads, block_cookie_banners, selector, wait_until, delay, cache |
| `list_devices` | No | Returns the device presets that viewport_device accepts, with their sizes. Costs nothing. | none |

Through MCP, `format` defaults to `jpeg` and `block_ads` and `block_cookie_banners` default to `true`. A file over 6 MB comes back as a download link instead of inline data, and a model cannot look at a link.

The same call by hand:

```bash
curl -X POST "https://curlshot.com/api/mcp" \
  -H "Authorization: Bearer YOUR_ACCESS_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "take_screenshot",
    "arguments": {
      "url": "https://example.com",
      "viewport_width": 390,
      "viewport_height": 844,
      "format": "webp"
    }
  }
}'
```

### 3. Machine-readable pages

| Address | What it is |
| --- | --- |
| `/llms.txt` | A short map of the site for language models, one line per page. |
| `/llms-full.txt` | Every docs page in one file. |
| `/agent-md` | How an agent should call the API, on one markdown page. |
| `/openapi.json` | The API described in OpenAPI 3.1, for tools that generate clients. |
| `/.well-known/mcp.json` | A card for the MCP server: its address, how to sign in and its tools. |
| `any page + .md` | The markdown twin of a docs, tool, pricing or blog page. The same pages answer with markdown when the request says Accept: text/markdown. |

More in the [guide for AI agents](https://curlshot.com/docs/guides/ai-agents), the [screenshot API overview](https://curlshot.com/tools/screenshot-api) and [capturing an element](https://curlshot.com/docs/element).

## Can a screenshot carry a prompt injection?

Yes. A screenshot contains whatever text the page shows, and a model reads it. OWASP lists prompt injection first in its Top 10 for LLM applications (LLM01:2025) and says indirect prompt injections "occur when an LLM accepts input from external sources, such as websites or files". For multimodal systems it warns that attackers could exploit interactions between modalities, "such as hiding instructions in images". Treat what the model reads on a page as untrusted data.

- **Label it as untrusted.** Tell the model, in the system prompt, that anything it reads in a screenshot or a web page is content to report on, never a command to follow.
- **Give the agent the least it needs.** A screenshot tool that can only fetch public pages cannot leak much. Keep tools that send email, spend money or read private data out of the same loop, or behind a confirmation.
- **Ask a person before risky actions.** If what the agent saw leads to an action with consequences, a human approves it first.
- **Check the output shape.** When you expect "yes or no and a reason", validate that in code. An answer that suddenly contains a link or a request is a signal.

## Glossary

- **Vision model:** A language model that accepts images as input and can describe or reason about what is in them. It reads a screenshot directly, without a separate text recognition step.
- **Multimodal:** Able to take more than one kind of input in the same request, for example text and images together.
- **Token:** The unit a model counts input and output in, and the unit you are billed in. For an image, vendors convert pixels to tokens with a published rule: Claude counts 28 px patches, the newer OpenAI models count 32 px patches.
- **MCP:** Model Context Protocol: an open standard for how an AI application discovers and calls tools. Messages are JSON-RPC, carried over stdio or over streamable HTTP.
- **Tool call:** A structured message in which the model asks its host program to run a named function with arguments, for example take_screenshot with a url. The result comes back as the next message.
- **Accessibility tree:** The structure a browser builds for assistive technology. Each object has a name, a description, a role and a state, such as a button named "Start free".
- **Viewport:** The visible area of a page in the browser window, measured in CSS pixels. A viewport screenshot captures only that first screen. A full-page screenshot captures everything down to the bottom.

## Questions

### Should I give an AI agent a screenshot or the HTML of a page?

It depends on the question. A screenshot shows layout, colour, images and charts, so use it for anything about how the page looks. Extracted text or markdown is smaller and exact, so use it for reading articles or documentation. Raw HTML is the largest and is for selectors and attributes. The accessibility tree is for deciding what an agent can click or type into.

### How many tokens does a screenshot use?

It depends on the pixel size and the model. On Claude the documented formula is ⌈width / 28⌉ × ⌈height / 28⌉, so a 1280 × 800 screenshot is 46 × 29 = 1,334 tokens and a 390 × 844 one is 434. On OpenAI's patch models it is ⌈width / 32⌉ × ⌈height / 32⌉ × 1.2, so 1,200 and 422. On Gemini 3 it is about 1,120 per image at the default media resolution. These are the rules as documented on 11 October 2026.

### What screenshot size is best for an LLM?

One viewport that the provider does not have to shrink. 1280 × 800 or 1024 × 768 fits the Claude standard tier untouched (1568 px long edge and 1568 visual tokens). For reading dense text, a 390 × 844 phone viewport keeps text large and costs about a third as much. Crop to one element when the question is about one part of the page.

### Can a model read a full-page screenshot?

Often not well. A 1280 × 6000 full-page capture is scaled down to 335 × 1568 on the Claude standard tier and 550 × 2576 on the high-resolution tier, which makes body text tiny. On gpt-6-astra with the default detail it is kept at full size and costs 9,024 tokens. For a long page, send several viewport captures or the page text instead.

### Does PNG, JPEG or WebP change the token cost of a screenshot?

No. Anthropic, OpenAI and Google all document image tokens as a function of pixel dimensions or a resolution setting, not file size. The format changes how many bytes you upload and how sharp small text is. PNG is lossless, and JPEG or WebP at a high quality setting is much smaller and usually still legible.

### How do I connect a screenshot tool to an agent with MCP?

Add the server to your MCP client configuration with its address and an Authorization header that carries your access key. CurlShot serves MCP at /api/mcp over streamable HTTP, with two tools: take_screenshot (url plus options such as viewport_width, selector and format) and list_devices. The client lists the tools and the model calls them when it needs to see a page.

### Can a screenshot contain a prompt injection?

Yes. A model reads any text that is visible in the image, including text written to look like an instruction. OWASP describes this as indirect prompt injection (LLM01:2025) and notes that instructions can be hidden in images. Treat what the model reads on a page as untrusted data, limit what the agent is allowed to do, and require human approval for risky actions.

## Sources

1. Anthropic, Claude API documentation, "Vision: image limits, resolution and token cost": https://platform.claude.com/docs/en/build-with-claude/vision (checked 11 October 2026)
2. Anthropic, Claude API documentation, "Coordinates and bounding boxes: how Claude resizes and pads images": https://platform.claude.com/docs/en/build-with-claude/vision-coordinates (checked 11 October 2026)
3. OpenAI, API documentation, "Images and vision: calculating costs": https://developers.openai.com/api/docs/guides/images-vision (checked 11 October 2026)
4. Google, Gemini API documentation, "Image understanding: token calculation": https://ai.google.dev/gemini-api/docs/image-understanding (checked 11 October 2026)
5. Google, Gemini API documentation, "Media resolution: token counts": https://ai.google.dev/gemini-api/docs/media-resolution (checked 11 October 2026)
6. OWASP Gen AI Security Project, "LLM01:2025 Prompt Injection": https://genai.owasp.org/llmrisk/llm01-prompt-injection/ (checked 11 October 2026)
7. Model Context Protocol, "Transports (specification 2025-06-18)": https://modelcontextprotocol.io/specification/2025-06-18/basic/transports (checked 11 October 2026)
8. MDN Web Docs, "Accessibility tree": https://developer.mozilla.org/en-US/docs/Glossary/Accessibility_tree (checked 11 October 2026)

## Go deeper

- [Guide: using the API from AI agents](https://curlshot.com/docs/guides/ai-agents)
- [Screenshot tool for AI agents](https://curlshot.com/tools/screenshot-tool-for-ai-agents)
- [Screenshot API overview](https://curlshot.com/tools/screenshot-api)
- [Docs: capturing an element](https://curlshot.com/docs/element)

## Read next

- [Viewport sizes cheat sheet: phones to desktops](https://curlshot.com/blog/viewport-sizes-cheat-sheet.md): Viewport sizes in CSS pixels, screenshot sizes and device pixel ratios of iPhone, Pixel, Galaxy, iPad and laptop screens, drawn to scale, and the sizes to test.
- [PNG, JPEG, WebP or PDF for website screenshots](https://curlshot.com/blog/png-jpeg-webp-pdf-for-screenshots.md): A decision flow, a comparison table and our own file-size measurements for choosing PNG, JPEG, WebP or PDF when you save a screenshot of a web page.
- [Screenshot API vs headless browser: the real cost](https://curlshot.com/blog/screenshot-api-vs-headless-browser.md): A cost calculator with every assumption editable, the hidden work behind headless Chrome, and an honest checklist for build or buy.
