Normalize text line endings
Normalize text line endings when a file, pasted value, generated document, or source-code fragment contains a mixture of Unix LF, Windows CRLF, and legacy standalone CR terminators.
Run — free
Choose LF or CRLF as the target, and the result preserves every visible character while making each line boundary consistent. The response includes the normalized text, the selected target, and an exact count of line terminators that required conversion, making the operation easy to verify and automate.
Why consistent line endings matter
Text can look identical in an editor while containing different byte sequences between its lines. Unix and modern macOS tools normally use a single line-feed character, represented as LF, while Windows commonly uses a carriage return followed by a line feed, represented as CRLF. Older files may also contain standalone carriage returns. Mixed endings often appear after copying between applications, combining generated and handwritten content, extracting archives, or accepting contributions from several operating systems. They can create noisy version-control diffs, confuse command-line tools, break strict importers, and make checksums differ even when the visible words match. This capability treats line boundaries as data that can be standardized deliberately. It recognizes CRLF as one terminator rather than two, also recognizes lone LF and lone CR, and converts all three forms to the requested style. Ordinary carriage returns are therefore interpreted as line endings, which is useful for legacy text but should be understood when processing unusual control-character data. No letters, spaces, tabs, or other characters are reformatted.
How conversion and counting work
Provide the complete text in the text field and select exactly LF or CRLF in the target field. The converter scans from beginning to end, matching a CRLF pair before it considers either character alone. That ordering prevents a Windows line ending from being counted twice. For an LF target, every CRLF pair and every standalone CR counts as one changed line ending, while an existing standalone LF remains unchanged. For a CRLF target, every standalone LF and standalone CR counts as one change, while an existing CRLF pair remains unchanged. The changed value reports terminators converted, not characters inserted or removed. For example, replacing one CRLF with LF is one change even though the output is one code unit shorter. Text without any line ending returns unchanged with a count of zero, as does text already using the chosen style. Empty text is valid because it is still a well-defined text value and requires no conversion. The operation is deterministic, so identical inputs always produce identical outputs.
Using the result safely in a workflow
Use the returned text directly as the canonical value for writing a file, calculating a digest, comparing revisions, feeding a parser, or sending content to another capability. Keep the changed count alongside migration logs when you need evidence that normalization occurred; a zero confirms that the input was already compliant, while a positive number tells you exactly how many boundaries were rewritten. The target field is repeated in the result so downstream steps do not need to rely on configuration stored elsewhere. This tool does not infer a preferred convention from a filename or operating system, because silent guessing makes automated pipelines harder to audit. Choose LF for repositories and Unix-oriented processing when that is your established convention, or CRLF for software and exchange formats that explicitly require Windows-style boundaries. If an unsupported target is supplied, the request fails instead of silently falling back. Normalization is limited to line terminators: it does not trim trailing spaces, change tabs, add a final newline, alter encoding, or rewrite Unicode normalization forms.
What you can do with it
Clean a source file before committing
Convert mixed endings to the repository convention so a small edit does not appear as a full-file rewrite.
Prepare text for a strict importer
Produce consistently terminated records before passing text to software that expects LF or CRLF exclusively.
Canonicalize content before hashing
Normalize line boundaries before calculating checksums so operating-system differences do not change the digest.
FAQ
What line-ending styles can I choose?
The supported targets are LF and CRLF. Any other target produces an invalid-input error.
Does a CRLF pair count as one change or two?
It counts as one line ending and therefore at most one change. The pair is recognized before standalone characters.
Will normalization add a newline at the end?
No. It converts existing line endings only and never adds or removes a final line boundary.
What happens to standalone CR characters?
Each standalone CR is treated as a legacy line ending and converted to the selected target style.
How much does an API request cost?
Each API request costs $0.002. The same deterministic conversion can also run in the browser.
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/data/normalize-line-endings \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"first line\r\nsecond line\nthird line\rfourth line","target":"LF"}'const res = await fetch("https://api.kit.forhosting.com/data/normalize-line-endings", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "first line\r\nsecond line\nthird line\rfourth line",
"target": "LF"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/data/normalize-line-endings",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "first line\r\nsecond line\nthird line\rfourth line",
"target": "LF"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/data/normalize-line-endings", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"first line\\r\\nsecond line\\nthird line\\rfourth line","target":"LF"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"first line\r\nsecond line\nthird line\rfourth line","target":"LF"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/data/normalize-line-endings", 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": "first line\r\nsecond line\nthird line\rfourth line",
"target": "LF"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "data.normalize_line_endings",
"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. |