Read the web as Markdown.
Any URL in, clean Markdown out, with an AIO score for how readable the page is to an agent. We read robots.txt first, and when a read comes back thin we say so rather than hand you an empty page.
One call, and what comes back.
1,000 free credits a month; a read is one of them. Failed reads are refunded.
$ curl https://api.buildonto.dev/v1/read \ -H "Authorization: Bearer onto_sk_live_…" \ -H "Content-Type: application/json" \ -d '{"url": "https://stripe.com/pricing"}'
{
"status": "success",
"url": "https://stripe.com/pricing",
"markdown": "# Pricing & Fees\n\n> Find Stripe fees…",
"metadata": { "title": "Pricing & Fees", … },
"stats": {
"raw_html_size_kb": 698.8,
"markdown_size_kb": 16.9,
"reduction_percent": 97.6, …
},
"cache": { "hit": false, "ttl_seconds": 3600 }
}One real read of stripe.com/pricing, 24 Sep 2026. Its AIO score, 90, comes from /v1/score or /v1/read-and-score.
The reader is open.
Put any URL after the host. Agents get plain Markdown; a browser gets a readable page. Same URL either way.
md.buildonto.dev/any-url, no sign-up. It's the same reader as /v1/read, with the same robots.txt check.
- 30 a minuteper IP address, with no total cap and no sign-up
- 1 houra page is cached, so a repeat doesn't fetch it again
- No JSit reads the HTML the server sends; client-rendered pages come back flagged
What you can call.
JSON in, JSON out, with Authorization: Bearer and your key. Scores never cost a credit.
- POST
/v1/readAny URL as clean Markdown1 credit - POST
/v1/scoreThe AIO score: how readable a page is to an agentFree - POST
/v1/read-and-scoreBoth, in one round trip1 credit - POST
/v1/batchUp to 50 URLs, or a whole site, in one call1 per URL - POST
/v1/mapA site's URLs, from its sitemap or links1 credit - POST
/v1/extractThe structured data a page declares (JSON-LD, OpenGraph, meta)1 credit - GET
/v1/usageYour quota and what you have usedFree - POST
/mcpThe hosted MCP server, for Claude, ChatGPT and any MCP clientAs the tool
When it fails, you get a name.
Every error comes back as { "status": "error", "code", "message" } with the HTTP status below.
INVALID_REQUESTThe body is missing a field or malformedINVALID_URLNot a parseable http(s) URL, or a private addressUNAUTHORIZEDMissing or invalid API keyPAYMENT_REQUIREDThe plan's credits are used and there are no pack credits leftPLAN_RESTRICTEDNot on your plan: credit packs need a paid planRATE_LIMITEDThe free tier's monthly credits are usedCONCURRENT_LIMITToo many requests in flight at once for your planROBOTS_BLOCKEDIts robots.txt disallows GPTBot or every crawler, and we honour thatWAF_BLOCKEDIt refused our fetcher (401 or 403)URL_NOT_FOUNDThe page 404s, or the host doesn't resolveNO_RESULTSMap or batch found no pages on itUNSUPPORTED_TYPENot a document: an image, video or archiveTOO_LARGEThe body is over the read limitIMAGE_PDFA scanned PDF with no text to extractTLS_ERRORIts certificate is invalidTIMEOUTIt took more than 15 seconds to answerEXTRACTION_FAILEDThe page loaded but we couldn't convert it