Translate Markdown
Documentation written in Markdown lives or dies by its syntax — a stray asterisk turns a heading into plain text, a mangled link reference breaks navigation across an entire docs site. This endpoint translates the prose and leaves every hash mark, bracket and fence exactly where the author put it.
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.
Why Markdown resists naive translation
Markdown's whole appeal is that lightweight punctuation carries structural meaning — a leading # is a heading, a pair of asterisks is emphasis, three backticks open a code block. A translator that doesn't understand that syntax will happily reflow a bulleted list into a paragraph or translate the word inside a link's brackets while leaving the URL pointing at an English-only page. translate.markdown parses the document as Markdown first, so translation only touches the human-readable text, never the syntax that gives it shape.
What moves and what stays put
Paragraph text, heading text, list item text, table cell content, and link labels are translated. Code fences and inline code spans are left untouched, since a code sample shown in a README should run exactly the same in every language. URLs, image paths, footnote identifiers, and front matter keys stay as written; only front matter values that are genuinely prose, like a page title or description, are eligible for translation.
A brief note on the format itself
Markdown was designed in the mid-2000s to let people write formatted text using nothing but a plain-text editor, and it became the default authoring format for READMEs, technical docs, and static site generators precisely because it stays readable even before it's rendered. That plain-text readability is also what makes it fragile under careless translation: unlike HTML, there's no explicit tag boundary telling a naive tool where formatting starts and ends, so getting Markdown translation right requires actually parsing the document's structure rather than treating it as a flat string.
Fitting into a docs pipeline
A documentation site with an English source of truth and several target locales usually needs translation to run every time a page changes, not just once at launch. Submitting the changed Markdown file to /translate/markdown on each merge and writing the translated result back to the corresponding locale folder keeps every language current without a human re-reading the whole page for drift. Pricing per word rather than per file means a one-line changelog update costs a fraction of what a full API reference page costs, so incremental documentation changes stay cheap to keep translated.
What you can do with it
Multilingual README files
An open-source project translates its README.md into several languages on every release, keeping code blocks and badge links identical across versions.
Developer documentation sites
A docs site built on a static site generator translates each Markdown page automatically when the English source changes, preserving front matter and internal links.
Changelog and release notes
A product team translates its Markdown changelog into customer-facing languages immediately after each release without re-formatting bullet points.
Knowledge base articles
An internal wiki written in Markdown gets translated for regional offices while table formatting and cross-references between articles stay intact.
FAQ
How do I translate a Markdown file with the API?
POST the Markdown content and target language to /translate/markdown, keep the returned task_id, and retrieve the translated file by webhook or a signed link valid for 24 hours.
Is the Markdown translation API free?
No, there's no free tier or trial; it costs $0.003 per request plus $0.0135 per 1000 words, and a failed task is never charged.
Will code blocks get translated by mistake?
No, code fences and inline code spans are always left untouched, since code samples must run identically regardless of the surrounding language.
Does it preserve headings, lists and tables?
Yes, heading levels, list structure and table layout are preserved exactly; only the readable text inside them is translated.
What about front matter at the top of the file?
Front matter keys stay unchanged, and only prose-like values such as a title or description are eligible for translation.
Can I translate an entire docs folder in bulk?
Yes, submit one async task per file and collect each translated page as it finishes, which fits well with a CI step that runs on every content change.
Is this endpoint live?
Yes, /translate/markdown is live and accepting requests now.
Is my documentation content stored after translation?
No, source and translated files are deleted after the retention window and are 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/translate/markdown \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"…"}'const res = await fetch("https://api.kit.forhosting.com/translate/markdown", {
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/translate/markdown",
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/translate/markdown", 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/translate/markdown", 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": "translate.markdown",
"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. |