Claude Reads Screenshots at 2,576px Now. The Same PNG Can Cost 3x the Tokens
Book: AI That Answers The series: AI in TypeScript — 5 books, from your first LLM call to agents in production — all five here My project: Hermes IDE | GitHub — an IDE for developers who ship with Claude Code and other AI coding tools Me: xgabriel.com | GitHub In April, Simon Willison fed one 3456×

Posted on Nov 5 • Edited on Nov 5 • AI-assisted
In April, Simon Willison fed one 3456×2234, 3.7 MB PNG screenshot into his Claude token counter. On Claude Opus 4.6 it came to 1,578 tokens. On Opus 4.7 the same file came to 4,744. That is a 3.01x jump for one image, at the same $5 per million input tokens on both models.
Same file, different model. Opus 4.7 accepts images up to 2,576 pixels on the long edge, about 3.75 megapixels, where earlier Claude models shrank anything bigger to fit 1,568. Anthropic's vision docs now put every model from Claude 4.7 on in that high-resolution tier, with no opt-in. If your app sends screenshots, the per-image cost you estimated on an older model can be a third of what the same PNG costs on a current one.
Small images didn't move. In the same post, a 682×318 image cost 314 tokens on 4.7 and 310 on 4.6. The extra cost lives only in big images, and the rule that decides it fits in one line of arithmetic.
Claude doesn't price pixels. The vision docs say it cuts the image into 28×28-pixel patches and charges one visual token per patch: ceil(width / 28) × ceil(height / 28). A 1000×1000 image is 36 × 36 = 1,296 tokens.
Then there is a ceiling. Each model has a long-edge limit and a visual-token budget, and an image over either one is shrunk, aspect ratio kept, until it fits:
The token budget is the limit that bites. Anthropic's coordinates guide says that for nearly all photos and screenshots the token limit sets the final size, not the edge: a 1920×1080 screenshot on a standard-tier model becomes 1456×819, not 1568×882. On a high-resolution model the same screenshot isn't resized at all and costs 2,691 tokens instead of 1,560.
Run Simon's screenshot through those rules with the guide's reference resize function and his numbers line up. On the standard tier, 3456×2234 shrinks to 1372×887: 49 × 32 = 1,568 patches, exactly the budget. On the high-resolution tier it shrinks to 2383×1540: 86 × 55 = 4,730 patches. That ratio is 3.02x against his measured 3.01x. This is my arithmetic, not a measurement; the 10 to 14 tokens his counts carry on top are most likely the rest of the request, which a token counter includes.
Claude bills per square tile, so a longer edge means more tiles on the bill.
The older shortcut, still quoted in write-ups like fast.io's, is width × height / 750, which that write-up calls Anthropic's established rule of thumb. Since 28 × 28 is 784, the two agree within a few percent on mid-sized images: a 1000×1000 image is 1,333 by the shortcut and 1,296 by patches. It drifts in three places.
It ignores the ceiling. 3456×2234 / 750 is 10,294 tokens, a number neither tier will ever bill, because the image is shrunk first. That estimate is about 2x too high for a high-resolution model and over 6x too high for a standard one.
It ignores rounding. Patches round up on each axis. 1120×700 is exactly 40 × 25 = 1,000 tokens. Add one pixel to each side, 1121×701, and it becomes 41 × 26 = 1,066. The shortcut moves by about 2.
It doesn't know your model's tier. Same image, same formula, up to 3x apart depending on the model ID in the request.
What doesn't matter at all is file size. Nothing in the formula looks at bytes, and the coordinates guide notes that the token counting endpoint estimates an image's cost from its dimensions without fully processing it. A 3.7 MB PNG and a 300 KB JPEG at the same dimensions cost the same tokens. Compression only changes latency and request size, and the vision docs warn that lossy JPEG or WebP can add artifacts that make text hard to read, which is the opposite of what you want from a screenshot. File size still matters for limits: each image is capped at 10 MB base64-encoded on the Claude API and 5 MB on Amazon Bedrock.
The token counting endpoint is free, rate-limited per usage tier (5,000 requests a minute on the Start tier), and works with every active model. It only counts images sent as base64, not by URL or file ID. That is enough to print your own table.
The script below uses the Anthropic TypeScript SDK and sharp. It resizes one screenshot to several long edges, counts each on two models, subtracts a text-only baseline so only the image is left, and prints tokens and dollars per thousand images. I haven't run this script myself, so treat the numbers it prints as the source of truth, not the ones in this post.
// count-image.mts // npm i @anthropic-ai/sdk sharp // ANTHROPIC_API_KEY=... npx tsx count-image.mts shot.png import Anthropic from " @anthropic-ai/sdk "; import sharp from " sharp "; import { readFile } from " node:fs/promises "; const client = new Anthropic (); const MODELS = [ { id: " claude-opus-4-6 ", usdPerMTok: 5 }, { id: " claude-opus-4-7 ", usdPerMTok: 5 }, ]; // long-edge targets in px; 0 = send the original size const EDGES = [ 0, 2576, 1568, 1092, 768 ]; const PROMPT = " What is on this screen? "; const patches = ( w: number, h: number ) => Math. ceil ( w / 28 ) * Math. ceil ( h / 28 ); async function count ( model: string, png?: Buffer ) { const text = { type: " text " as const, text: PROMPT }; const content = png? [ { type: " image " as const, source: { type: " base64 " as const, media_type: " image/png " as const, data: png. toString ( " base64 " ), }, }, text, ]: [ text ]; const res = await client. messages. countTokens ({ model, messages: [{ role: " user ", content }], }); return res. input_tokens; } Enter fullscreen mode Exit fullscreen mode Resize with sharp to each target long edge, never upscaling:
async function atEdge ( src: Buffer, edge: number ) { const img = sharp ( src ); if ( edge > 0 ) { img. resize ({ width: edge, height: edge, fit: " inside ", withoutEnlargement: true, }); } return img. png (). toBuffer (); } Enter fullscreen mode Exit fullscreen mode The main loop counts every size on both models and prints the table:
const src = await readFile ( process. argv [ 2 ]); const rows: Record < string, unknown > [] = []; for ( const m of MODELS ) { const baseline = await count ( m. id ); for ( const edge of EDGES ) { const png = await atEdge ( src, edge ); const meta = await sharp ( png ). metadata (); const w = meta. width?? 0; const h = meta. height?? 0; const tokens = ( await count ( m. id, png )) - baseline; rows. push ({ model: m. id, sent: ` ${ w } x ${ h } `, kb: Math. round ( png. length / 1024 ), predicted: patches ( w, h ), counted: tokens, usdPer1k: (( tokens * m. usdPerMTok ) / 1000 ). toFixed ( 2 ), }); } } console. table ( rows ); Enter fullscreen mode Exit fullscreen mode The kb column will swing a lot between rows and the counted column won't follow it, because bytes aren't in the formula.
And predicted is the patch count of the pixels you sent: when counted lands on or near it, Claude saw your image at the size you sent. When counted is well below it, the server shrank the image to its tier's budget, and you paid for pixels you could have dropped yourself. For a 3456×2234 shot on Opus 4.7, the original and the 2,576 rows should both land near 4,730, the size the server shrinks this shot to.
A 2576×1665 image is still 92 × 60 = 5,520 patches, over the 4,784 budget.
Here is what one screenshot's image tokens cost per thousand requests, computed from the docs' token table and each model's list input price. It leaves out your prompt text and the output tokens.
The Sonnet rows are the ones to stare at. Sonnet 5.5's input price is a third lower than Sonnet 4.6's, and a 4K screenshot costs about twice as much on it. Each token got cheaper, but the same image now takes about three times as many of them.
Same screenshot, same file: the model's resolution tier decides how heavy the bill is.
If you don't need the extra detail, shrink before you send. Anthropic's docs say it directly: downsample when you don't need the fidelity, and resize yourself to cut latency. Copy resizedSize from the coordinates guide (it ships a TypeScript version) and pin screenshots to the old standard budget on any model:
// resizedSize() is Anthropic's reference function, // copied from the vision-coordinates guide. const STANDARD = { edge: 1568, tokens: 1568 }; async function pinToStandard ( src: Buffer ) { const meta = await sharp ( src ). metadata (); const [ w, h ] = resizedSize ( meta. width?? 0, meta. height?? 0, STANDARD. edge, STANDARD. tokens, ); return sharp ( src ). resize ({ width: w, height: h, fit: " fill " }). png (). toBuffer (); } Enter fullscreen mode Exit fullscreen mode Don't do it blind, though. The same docs say the high-resolution tier exists for computer use, screenshot understanding and dense documents, and small text is the first thing a downscale eats. Run your real screenshots through both sizes, check the answers, and only then pick the budget.
Screenshot apps hit a few more rules. A request with more than 20 images triggers a stricter per-image size limit, and the vision docs say to keep both sides at 3,000 px or under to stay clear on every platform. Screenshots returned to the computer use and browser use tools are rejected with a validation error when they exceed the model's limits instead of being downscaled. And if a silent server-side resize would break your pipeline, set "transformations": { "oversized_image": "error" } on the image block: the API, and the token counting endpoint, then reject the image instead of shrinking it, per the coordinates guide.
The habit that saves you is boring. Before a feature that takes screenshots ships, count one real screenshot on the exact model ID you'll deploy, write the per-image number next to the feature, and recount when you switch models.
Per-request token cost is one of the first things a working LLM app has to get right, and it's a big part of what AI That Answers covers: your first LLM app in TypeScript, from prompts and structured output to knowing what each call costs before your users find out for you. If you want the screenshot feature above to ship with a budget instead of a surprise, that book is where I'd start.
It's book one of AI in TypeScript, five books from your first LLM call to agents in production: the whole series is here.