Web page to Markdown

The same render as a screenshot — JavaScript executed, consent banners dismissed, lazy images loaded — read as Markdown instead of photographed.

Quickstart

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)
Need a key? Create a free account — 500 renders a month, no card, no confirmation email. Pass it as access_key=... or an X-Access-Key header. Paid plans start at $17 for 2,000 — pricing. Or try it with no key at all on the homepage demo.

Why not just fetch the HTML?

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.

Options

ParameterDefaultWhat it does
strip_chrometrueDrop nav, header, footer and sidebars. Falls back to the whole body if stripping would leave nothing.
max_chars0Truncate to a fixed context budget. 0 means no limit.
responsemarkdownjson returns url, title, chars and markdown instead.

Everything from /take still applies — block_ads, basic_auth, headers, timeout, signed URLs.

MCP server

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.

It is hosted: connect by URL, nothing to install. In Claude Code:

claude mcp add --transport http screenshotline https://api.screenshotline.com/mcp --header "X-Access-Key: YOUR_KEY"

Any client that takes a remote server as JSON (Cursor and the rest):

{
  "mcpServers": {
    "screenshotline": {
      "url": "https://api.screenshotline.com/mcp",
      "headers": { "X-Access-Key": "YOUR_KEY" }
    }
  }
}

If a client cannot send headers, put the key on the URL instead: https://api.screenshotline.com/mcp?access_key=YOUR_KEY. Tool calls use the same key, quota and billing as /take — a render through MCP is a render.

Self-hosting? The same server runs locally over stdio from a clone of the repo: claude mcp add screenshotline -- node /path/to/screenshotline/mcp/server.js.

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.