HTML to text
Raw HTML is full of markup a search index, a summarizer or a human reader never asked for. This endpoint pulls the actual words out of the tags, keeping paragraph breaks and list order intact instead of dumping one flat wall of text.
Run — free
Runs in your browser. Free, unlimited — your data never leaves this page.
Markup is noise for most downstream jobs
A scraped page, a saved newsletter or a CMS export all carry the same problem: the words you want are wrapped in div soup, inline styles, script tags and attributes that mean nothing to a language model, a search index or a plain-text digest. Stripping tags with a quick regex breaks the moment a script block or a comment contains a stray angle bracket, and it flattens headings and list items into one unreadable run-on. data.html_to_text parses the document instead of pattern-matching it, so the output reads the way a person skimming the page would read it.
What the request does
Send POST /data/html-to-text with the HTML source, and the response is a task_id while extraction runs in the background. The finished plain-text result — with paragraph breaks preserved and list items kept on their own lines — arrives through a signed webhook call or waits behind a signed link valid for 24 hours.
Structure survives, tags don't
The difference between good extraction and a naive strip is what happens to structure: a heading should still start its own line, a bullet list should still read like a list, and a table row shouldn't collapse into one run-on sentence with no spaces between cells. HTML itself was never meant to carry meaning beyond layout — it's a presentation language from the early web, and browsers have spent thirty years being forgiving of malformed markup, which is exactly why a real parser handles edge cases that string replacement cannot.
Where it fits in automation
Search indexing, retrieval pipelines feeding a language model, email digest generators and full-text diffing tools all want clean text, not markup, and none of them should be maintaining their own HTML parser to get it. Because the task is billed per request and never charged on failure, a crawler can push every fetched page through this endpoint without budgeting for wasted calls, and the async design means a batch of ten thousand pages just becomes ten thousand webhook deliveries arriving on their own schedule instead of one script blocking on each page in turn.
What you can do with it
Feeding a search index
A site search tool converts crawled HTML pages to clean text before indexing, so query matches land on real content instead of on markup artifacts.
Preparing context for a language model
A retrieval pipeline strips HTML from fetched web pages so only readable text goes into the model's context window, cutting wasted tokens.
Plain-text email digests
A newsletter aggregator converts each HTML article to text to build a lightweight plain-text summary email with no broken layout.
Content diffing over time
A monitoring tool extracts text from a page on each crawl and diffs the plain text, avoiding false positives from cosmetic markup changes.
FAQ
How do I convert HTML to text with the API?
POST your HTML to /data/html-to-text, keep the returned task_id, and collect the plain-text result by webhook or a signed link valid for 24 hours.
Is the HTML to text API free?
No, there is no free tier or trial; it costs $0.002 per request, and a failed extraction is never charged.
Does it preserve paragraph and list structure?
Yes, paragraph breaks and list items are kept on separate lines instead of being collapsed into one run-on block of text.
Will it strip script and style tags too?
Yes, script, style and other non-visible elements are removed along with their content, so only readable text remains.
Can I use this to clean scraped pages for a search index?
Yes, that's one of its most common uses — stripping markup before indexing avoids false matches on layout code.
Can I process HTML in bulk?
Yes, submit one async task per document and collect each result by webhook as it completes, which suits large crawl batches.
Is this endpoint live?
Yes, data.html_to_text is live and accepting requests now.
Is my HTML stored after processing?
No, submitted HTML and the extracted text are deleted after the retention window and never used for training.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/data/html-to-text \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"input":"…"}'const res = await fetch("https://api.kit.forhosting.com/data/html-to-text", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"input": "…"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/data/html-to-text",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"input": "…"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/data/html-to-text", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"input":"…"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"input":"…"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/data/html-to-text", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"input": "…"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "data.html_to_text",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_mb | 25 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |