Normalize Hyphens and Dashes
Text copied from websites, PDFs, office documents, and publishing systems often contains several characters that look like dashes but behave differently in search, validation, sorting, and code.
Run — free
This dash normalizer converts common Unicode hyphens, en dashes, em dashes, minus signs, and related variants into one consistent style. Choose an ordinary hyphen, an en dash, or an em dash, then receive clean text plus an exact count of changed characters safely.
Why visually similar dashes cause practical problems
A hyphen-minus, en dash, em dash, mathematical minus sign, and nonbreaking hyphen may look nearly identical at a glance, especially in a proportional font. Computers do not consider them interchangeable. A product code copied from a PDF can fail an exact database lookup because it contains a nonbreaking hyphen. A negative value pasted from typesetting software may not parse as a number because its sign is a mathematical minus rather than the ASCII character expected by an importer. Search indexes, regular expressions, duplicate detection, filenames, and command-line tools can all react differently to these code points. The problem becomes harder to inspect when the unusual character is invisible, as with a soft hyphen inserted for line breaking. Normalization removes that ambiguity by replacing recognized dash-family characters with one chosen character. The surrounding letters, numbers, punctuation, whitespace, and line breaks remain unchanged, so the operation is focused and predictable rather than a broad typographic rewrite.
Choose the dash style that matches your destination
Use the hyphen style for identifiers, imported data, plain-text interchange, search keys, filenames, source code, and systems that expect ASCII. It is the safest choice when compatibility matters more than typography. Choose the en dash when preparing editorial text whose house style uses it for numeric ranges or relationships, such as page ranges and routes. Choose the em dash when a publishing workflow wants a consistent sentence-level dash. The selected character replaces every recognized dash in the submitted text, including existing ASCII hyphens when the target is an en or em dash. That uniform behavior is intentional: the tool normalizes characters, but it does not infer whether a particular mark represents a compound word, subtraction, a range, or a parenthetical break. Such inference would be language-dependent and could silently alter meaning. If your document needs different dash styles for different grammatical roles, normalize only the relevant selections or apply contextual editorial rules after this mechanical cleanup step.
Use deterministic output in cleanup and validation workflows
The response includes the normalized text, the selected style, the literal target character, and the number of characters that actually changed. A dash already matching the target is retained and is not counted as a replacement. This makes the result useful in automated quality checks: a pipeline can reject unexpected typography, record how much source material was altered, or confirm that a second normalization pass reports zero changes. The algorithm is deterministic and performs no network requests, language detection, artificial intelligence, or random transformation. It scans a bounded text input and replaces only a documented family of Unicode dash-like characters. Whitespace is deliberately preserved; an em dash surrounded by spaces keeps those spaces when converted to a hyphen, and an unspaced dash remains unspaced. That avoids combining character normalization with subjective spacing policy. For repeatable imports, run this operation before tokenization, exact matching, numeric parsing, slug creation, or deduplication, then apply any domain-specific punctuation and spacing rules separately.
What you can do with it
Clean imported identifiers
Convert hidden nonbreaking hyphens and typographic dashes before matching product codes, account references, or document IDs.
Prepare reliable plain text
Standardize punctuation copied from PDFs and office documents before feeding it to search, validation, or data-processing tools.
Enforce an editorial dash style
Convert mixed dash characters to a single en dash or em dash style before a focused copy-editing pass.
FAQ
What does it cost?
It is free to run in your browser on this page, or $0.002 per API request.
Which output styles are available?
You can normalize recognized characters to an ASCII hyphen-minus, an en dash, or an em dash.
Does it change spaces around a dash?
No. Existing whitespace and line breaks are preserved exactly; only recognized dash characters are replaced.
Does it decide whether a dash is punctuation, a range, or subtraction?
No. It performs mechanical character normalization and does not infer grammatical or mathematical meaning.
What does the replacement count mean?
It counts recognized characters whose original code point differed from the selected target character.
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/normalize-dashes \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Pages 10–15 — revised − 2 notes"}'const res = await fetch("https://api.kit.forhosting.com/str/normalize-dashes", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "Pages 10–15 — revised − 2 notes"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/str/normalize-dashes",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "Pages 10–15 — revised − 2 notes"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/str/normalize-dashes", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"Pages 10–15 — revised − 2 notes"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"Pages 10–15 — revised − 2 notes"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/str/normalize-dashes", 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": "Pages 10–15 — revised − 2 notes"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "str.normalize_dashes",
"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_chars | 200000 |
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. |