The same render as a screenshot — JavaScript executed, consent banners dismissed, lazy images loaded — read as Markdown instead of photographed.
curl "https://api.screenshotline.com/extract?url=https%3A%2F%2Fexample.com&access_key=YOUR_KEY"
# Example Domain
This domain is for use in documentation examples without needing permission.
[Learn more](https://iana.org/domains/example)
Because on a great many sites there is nothing useful in it. A plain fetch of
app.netlify.com returns 121 characters of visible text, and they read
"The Netlify dashboard needs JavaScript :(". Rendered first, the same URL
returns the actual page.
The second reason is subtler. We read the rendered layout, not the markup.
danluu.com writes <d>09/26</d><a>title</a>
with no whitespace between them and styles the first as a 4em flex item. Parsed as markup
that concatenates into 09/26[title]; read with the computed layout it comes
out as 09/26 [title], which is what a human sees.
Open shadow roots are walked too, so pages built out of web components extract as text rather than as nothing.
| Parameter | Default | What it does |
|---|---|---|
| strip_chrome | true | Drop nav, header, footer and sidebars. Falls back to the whole body if stripping would leave nothing. |
| max_chars | 0 | Truncate to a fixed context budget. 0 means no limit. |
| response | markdown | json returns url, title, chars and markdown instead. |
Everything from /take still applies — block_ads,
basic_auth, headers, timeout, signed URLs.
Agents can call this directly over the Model Context Protocol. Two tools:
screenshot for when the layout is the answer, read_page for when
the text is.
claude mcp add screenshotline -- npx screenshotline-mcp
A blank capture comes back with an explicit warning rather than as a silent empty
picture, and the SSRF guards apply on this path too — an agent cannot talk it into
probing 127.0.0.1 or a cloud metadata endpoint.