Remove Unicode combining marks from text
This tool removes combining diacritical marks from Unicode text after canonical decomposition.
Run — free
Accented letters such as é, ñ, and ü become their base forms, while punctuation, spacing, symbols, letter case, and unrelated scripts remain intact. The result is useful when a search index, identifier, filename, or URL slug needs an accent-insensitive representation without applying broader transliteration or language-specific spelling rules. Processing is deterministic, local to the request, and returns a simple text value that can be copied or passed directly into another normalization step.
What the transformation does
Unicode can represent many accented letters in more than one way. A character such as é may be stored as one precomposed code point, or as a base letter e followed by a combining acute accent. Visual comparison alone cannot reveal which representation is present. This capability first applies canonical Unicode decomposition, commonly called NFD, so characters with canonical decompositions are expressed as base characters followed by their marks. It then removes characters in every Unicode mark category. The result turns examples such as café, piñata, and Ångström into cafe, pinata, and Angstrom. It does not lowercase text, collapse whitespace, remove punctuation, or replace symbols. It also does not promise full ASCII transliteration: letters without a canonical decomposition, such as ł, remain unchanged. That narrow behavior is intentional. It gives callers a predictable normalization primitive that can be combined with separate lowercase, punctuation, or slug formatting rules without hiding extra transformations inside one operation.
Building stable search keys and slugs
Accent-insensitive search often works best when the original value and a normalized key are stored separately. Keep the source spelling for display, then pass it through this capability to produce a comparison field. A query can receive the same treatment before matching, allowing a person who types cafe to find café without losing the correctly accented label shown in results. Slug pipelines can use the output as an early stage: remove marks first, then lowercase, replace spaces, and apply the project’s own punctuation policy. Ordering matters because decomposition exposes marks that would otherwise remain attached to precomposed letters. The operation is deterministic, so identical Unicode input always produces identical output and can safely be used in cache keys or repeatable data preparation. However, normalization is not a substitute for locale-aware collation. Languages disagree about whether accented letters are variants or distinct letters, so applications should retain the original text and choose accent-insensitive matching only where that behavior fits the product and its users.
Boundaries, scripts, and verification
The removal rule covers combining marks across Unicode rather than only the familiar accent range used by Western European languages. That makes it technically consistent, but it also means the effect can be substantial in writing systems where marks carry essential information. Test representative content from every language your application accepts before applying the result to search, routing, or identity fields. Emoji variation selectors and other characters classified as marks may also be removed, so the output should be treated as a normalized key rather than a display-perfect copy. Canonical decomposition is deliberately different from compatibility decomposition: decorative or compatibility forms are not broadly flattened, and the tool avoids pretending to be a general transliterator. For verification, compare both precomposed and decomposed spellings of the same sample and confirm that their results match. Also include punctuation, non-Latin text, emoji, and letters without decompositions in your tests. Those cases reveal whether this focused operation is sufficient or whether your pipeline needs additional, explicitly chosen normalization stages.
What you can do with it
Create accent-insensitive search keys
Store a normalized companion value so unaccented queries can match correctly accented names and terms.
Prepare text for URL slugs
Flatten decomposable accented letters before applying lowercase, separator, and punctuation rules.
Compare alternate Unicode representations
Normalize precomposed and combining-mark spellings into the same base-letter representation for deterministic comparison.
FAQ
What does this capability remove?
It applies canonical Unicode decomposition and removes characters in Unicode mark categories, including combining diacritical marks.
Does it convert all text to ASCII?
No. Characters without a canonical base-letter decomposition remain unchanged, as do punctuation, symbols, and letters from other scripts.
Does it lowercase or format a complete slug?
No. Lowercasing, whitespace replacement, punctuation filtering, and separator rules should be applied as explicit later steps.
Will precomposed and decomposed accents produce the same result?
Yes. Canonical decomposition happens before mark removal, so equivalent spellings such as a precomposed é and e plus a combining acute accent flatten consistently.
What does an API request cost?
Each API request costs $0.002. The transformation is also suitable for the generated browser executor because it requires no network or server state.
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, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/str/remove-diacritic-marks \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Crème brûlée — déjà vu in São Paulo"}'const res = await fetch("https://api.kit.forhosting.com/str/remove-diacritic-marks", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "Crème brûlée — déjà vu in São Paulo"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/str/remove-diacritic-marks",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "Crème brûlée — déjà vu in São Paulo"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/str/remove-diacritic-marks", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"Crème brûlée — déjà vu in São Paulo"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"Crème brûlée — déjà vu in São Paulo"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/str/remove-diacritic-marks", 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": "Crème brûlée — déjà vu in São Paulo"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "str.remove_diacritic_marks",
"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.
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. |