Text to table
Some information is written as a paragraph but actually wants to be a table — specs stacked in a sentence, options compared in prose, a list of names with attributes trailing after each one. This endpoint spots that structure hiding inside the prose and reformats it as a proper Markdown table, aligned columns and all.
Run it online
Run this on our servers with your account. Free tools run in your browser; this one bills your KIT balance per the price above.
The tell-tale sign your paragraph should be a table
You can usually feel it before you can name it: a paragraph that lists three products and, for each, mentions a price and a size, or a sentence that walks through five settings and what each one does. That is tabular data wearing a paragraph's clothes, and reformatting it by hand means retyping every value into a grid, which is exactly the kind of mechanical work worth automating. text.to_table reads the prose, recognizes the repeating attributes, and lays them out as columns and rows.
What happens between input and output
POST /text/to-table analyzes the text for repeated entities with shared attributes — the same kind of thing mentioned several times, each mention carrying comparable details — and infers sensible column headers from what is actually present in the source. The result comes back as clean Markdown table syntax, pipes and alignment included, so it drops straight into a wiki page, a GitHub README, or any Markdown-rendering surface without reformatting.
Markdown tables as the connective tissue of technical writing
Markdown became the default language of technical documentation precisely because its table syntax is plain text that any tool can render, diff, or version-control without a binary format getting in the way. That same plainness is why it's tedious to type by hand — aligning pipes and dashes across many rows is fiddly work that nobody enjoys doing twice. The endpoint produces syntactically correct tables every time, so the tedium disappears without losing the format's portability.
Where prose genuinely resists becoming a table
Not every paragraph should become a table, and the endpoint reflects that honestly: text with no repeating structure, or comparisons that do not share a consistent set of attributes, will not produce a clean table because forcing one would misrepresent the source. The task is meant for prose that already has a hidden grid inside it, not for turning arbitrary narrative into rows for its own sake.
Fitting into a documentation workflow
Teams that maintain comparison pages, changelogs, or spec sheets often keep source notes as prose because that's how the information first arrives, then need it as a table for publishing. Running this asynchronously as part of a documentation build means the table gets generated the moment the source notes are updated, keeping the published table and the underlying notes from drifting apart.
What you can do with it
Comparison pages from vendor notes
Convert prose comparing several tools or plans into a side-by-side Markdown table for a documentation or pricing page.
Changelog entries with mixed detail
Turn a paragraph describing several bug fixes, each with a version number and component, into a scannable release-notes table.
Spec sheets drafted in prose first
Reformat a product description that lists dimensions, weight, and material for several variants into a clean spec table for a README or wiki.
Meeting recaps with per-person updates
Convert a status-update paragraph mentioning several team members and their current tasks into a table for a project wiki.
FAQ
How does the text to table API decide on column headers?
It infers headers from the attributes that actually repeat across the text — the kind of thing mentioned and the details attached to each mention — rather than using a fixed template.
What table format does it output?
Standard Markdown table syntax with pipes and alignment rows, ready to paste directly into any Markdown-rendering tool, wiki, or README.
Will it force a table out of text that isn't actually tabular?
No. If the source text doesn't have a repeating, comparable structure, the endpoint won't fabricate columns just to produce a table, since that would misrepresent the source.
Is there a free trial for converting text to table?
There's no free tier — free tiers get abused and slow everyone down. Access runs on a prepaid ForHosting KIT balance: top up from $10.00 (it never expires) and each request is charged at its published price, so a call with no balance returns HTTP 402. No subscription, no tokens, no invented credits, and a failed task is never charged.
What's the cost per conversion?
$0.003 per request plus $0.0135 per 1,000 words of source text. Failed conversions are never charged; the system retries automatically up to three times first.
Can I convert many documents to tables in bulk?
Yes, queue as many asynchronous requests as needed and collect each resulting table by webhook as it completes.
How do I retrieve the finished table?
Via a signed webhook, recommended for pipelines, or a signed link valid for 24 hours if you prefer to fetch it manually.
Can it output CSV instead of Markdown?
This endpoint is focused on Markdown table output for documentation use; for spreadsheet-style structured data, a JSON or structured-data endpoint is the better fit.
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/text/to-table \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"…"}'const res = await fetch("https://api.kit.forhosting.com/text/to-table", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "…"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/text/to-table",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "…"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/text/to-table", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"…"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"…"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/text/to-table", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"text": "…"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "text.to_table",
"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_tokens | 20000 |
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. |
422 | task_failed | The task failed after 3 retries. You are never charged for it. |