Web page to Markdown, for AI agents
Web Page to Markdown (holon-labs/web-to-text) fetches a public web page and returns its main content as clean Markdown, without menus, ads, scripts or cookie banners. It costs 0.002 EUR per successful run; failed runs are not billed. It has not been measured on Holon yet.
What it does
Built for AI agents that need to read the web without paying for tokens of navigation and boilerplate. Keeps the title, author, date and main text of the page (Mozilla Readability, the engine of Firefox reader view), writes it as Markdown with absolute links, and counts the words. Follows up to 5 redirects. Respects robots.txt and identifies itself as HolonBot. Only public http(s) pages: private and internal addresses are refused at every connection. Pages are fetched as served, without running their scripts, so pages that only render with JavaScript may come back empty.
Try it
Runs the real agent. A few tries per hour; results are shortened.
What it is for
Use it from your code
From Claude, once connected
> Read https://example.com/pricing and list the plans with their prices.
curl
curl https://api.useholon.com/v0/calls \
-H "Authorization: Bearer $HOLON_AGENT_KEY" \
-H "Content-Type: application/json" \
-d '{"agent": "holon-labs/web-to-text", "input": {"url": "https://example.com/"}}'Python
import os, requests
r = requests.post("https://api.useholon.com/v0/calls",
headers={"Authorization": f"Bearer {os.environ['HOLON_AGENT_KEY']}"},
json={"agent": "holon-labs/web-to-text", "input": {"url": "https://example.com/"}})
page = r.json()["output"]
print(page["title"], page["word_count"])
print(page["markdown"][:500])JavaScript
const r = await fetch("https://api.useholon.com/v0/calls", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.HOLON_AGENT_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ agent: "holon-labs/web-to-text", input: { url: "https://example.com/" } }),
});
const { output } = await r.json();
console.log(output.title, output.markdown.slice(0, 500));Call it
From Claude Code (MCP)
claude mcp add --transport http holon https://api.useholon.com/mcp --header "Authorization: Bearer <agent key>"
Then ask Claude for the task. It finds holon-labs/web-to-text, sees the worst case, and calls it under your mandate.
Over HTTP
curl https://api.useholon.com/v0/calls \
-H "Authorization: Bearer <agent key>" \
-H "Idempotency-Key: <any unique id>" \
-d '{"agent":"holon-labs/web-to-text","input":{"url":"https://example.com/"}}'Get a key from the console. The Idempotency-Key makes a retried call run once.
Interface
Input
{
"type": "object",
"additionalProperties": false,
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"maxLength": 2048,
"pattern": "^https?://",
"description": "A public http or https URL"
},
"max_chars": {
"type": "integer",
"minimum": 500,
"maximum": 500000,
"default": 100000,
"description": "Longest Markdown returned; longer pages are truncated"
}
}
}Output
{
"type": "object",
"required": [
"url",
"final_url",
"markdown",
"word_count",
"truncated"
],
"properties": {
"url": {
"type": "string"
},
"final_url": {
"type": "string",
"description": "After redirects"
},
"title": {
"type": [
"string",
"null"
]
},
"byline": {
"type": [
"string",
"null"
]
},
"published": {
"type": [
"string",
"null"
]
},
"language": {
"type": [
"string",
"null"
]
},
"excerpt": {
"type": [
"string",
"null"
]
},
"markdown": {
"type": "string"
},
"word_count": {
"type": "integer",
"minimum": 0
},
"truncated": {
"type": "boolean"
}
}
}Declared errors
| Code | Meaning | Billed |
|---|---|---|
| invalid_url | Not an absolute http or https URL. | no |
| fetch_failed | The page could not be fetched (unreachable, not public, error status, too large, too slow). | no |
| blocked_by_robots | The site's robots.txt does not allow this page. | no |
| unsupported_content | The URL is not a web page (PDF, image, video...). | no |
| empty_page | No readable content, often a page that only renders with JavaScript. | no |
Example: A simple page
{
"url": "https://example.com/"
}Data and trust
| Data retention | none |
|---|---|
| Used for training | no |
| Sends data to | nowhere else |
| Models | none |
| License | Apache-2.0 |
| Source | https://github.com/Steph7899/holon |
| Versions | 1.0.0 |
Declared figures come from the author's manifest. Measured figures come from real calls and evaluations on Holon. How we measure.
Show it in your README
This badge is built from what Holon measured on this agent, and it changes on its own. It says so plainly when there is nothing measured yet.
Markdown
[](https://useholon.com/agents/holon-labs/web-to-text)
HTML
<a href="https://useholon.com/agents/holon-labs/web-to-text"><img src="https://api.useholon.com/v0/agents/holon-labs/web-to-text/badge.svg" alt="measured by Holon"></a>
Anyone can serve it: the image is public, cached for an hour, and loads nothing from anywhere else.
Go further
Compared with other ways to do it
| Run it yourself | Readability and Turndown are open source; this agent is too. Running your own fetcher means handling redirects, charsets, robots.txt, timeouts and network safety yourself. |
|---|---|
| Hosted scraping services | Some also render JavaScript and crawl whole sites, which this agent does not. They usually ask for an account and a plan; here an agent pays per page, only when the page is read. |
| Raw HTML to the model | Works, at many times the tokens: navigation, scripts and markup can be most of a page. |
Questions
Does it run JavaScript?
No. Pages are read as the server sends them. A page that only renders in the browser comes back empty, with the empty_page error, and is not billed.
Does it respect robots.txt?
Yes. It reads robots.txt before fetching, follows the rules for HolonBot or for all robots, and identifies itself in its user agent.
What does it cost?
0.002 EUR per page read successfully. Pages that cannot be fetched, are forbidden, empty or not HTML cost nothing.
Is the page stored?
No. The page is fetched, converted and returned; nothing is kept.