HTML to Markdown
POST /api/v1/html-to-markdown — turn one web page into clean Markdown, ready to feed a
model or render in your own app.
Request
{
"url": "https://en.wikipedia.org/wiki/Markdown",
"low_cost": true,
"fresh": false
}
| Field | Default | Notes |
|---|---|---|
url | — | Required. An http or https URL, up to 2048 characters. |
low_cost | true | Refuse rather than spend the higher conversion cost. Send false to allow it. |
fresh | false | Bypass the cache and fetch the page live. Slower. |
Response
200 with the conversion and its metadata:
{
"url": "https://en.wikipedia.org/wiki/Markdown",
"markdown": "**Markdown** is a lightweight markup language…",
"metadata": {
"title": "Markdown - Wikipedia",
"description": "…", "author": "…", "published": "2026-02-11T09:00:00Z",
"siteName": "Wikipedia", "language": "en",
"sourceURL": "https://en.wikipedia.org/wiki/Markdown"
},
"words": 2708,
"cost": 1,
"tookMs": 1238
}
metadata is best-effort — only title and sourceURL are
guaranteed, so read the rest defensively. A conversion served from cache also carries
"cached": true.
Pricing
Two published prices apply (both shown with your plan):
- Per conversion. The response's
costfield tells you what it cost —1for a standard page,2for one that needed extra work to retrieve (for example a page that only renders in a browser, or refuses ordinary access). Acostof2bills two conversion units. - Per failed attempt. A page that was actually fetched and then refused bills one unit at the failed-attempt price — a fraction of a conversion.
Because low_cost defaults to true, you are never charged the higher
rate unless you ask for it: a page that would cost 2 comes back as
422 cost_limit instead. Retry with "low_cost": false if the page is worth it.
Example
curl https://khabot.com/api/v1/html-to-markdown \
-H "Authorization: Bearer kb_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://en.wikipedia.org/wiki/Markdown"}'
Errors
A refusal is a fact about the page, so it keeps its meaning. The six page-level codes below bill one failed unit; anything that never reached a page — including our own faults — is free.
| HTTP | code | Charge | Meaning | What to do |
|---|---|---|---|---|
| 422 | cost_limit | 1 failed unit | Needs the higher cost; details.required_cost says what. | Retry with low_cost:false if the page is worth it. |
| 422 | page_blocked | 1 failed unit | The site refuses automated access. | Don't retry — use another source. |
| 422 | page_thin | 1 failed unit | Reachable but no article to extract; details.words. | Don't retry — index, landing or login page. |
| 422 | page_unsupported | 1 failed unit | Not an HTML page; details.content_type. | Don't retry. |
| 404 | page_not_found | 1 failed unit | The page no longer exists. | Don't retry — the URL is dead. |
| 503 | page_fetch_failed | 1 failed unit | The page could not be retrieved. | Safe to retry once shortly. |
| 400 | bad_request | free | url missing, malformed, not http(s) or too long — or low_cost/fresh aren't booleans. | Fix the request. |
| 503 | upstream_unavailable | free | Our backend is unavailable — nothing to do with your URL. | Retry shortly. |
| 503 | service_not_configured | free | The service isn't switched on. | Contact support. |
The usual account-level codes apply too — see Errors.