Find common lines in two texts
Compare apple, banana, cherry, banana, pear with pear, banana, orange, banana and get banana and pear: each shared line once, in the order of the first list. Matching is exact by default, with an option to ignore capitals. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Text a
- apple banana cherry banana pear
- Text b
- pear banana orange banana
- Text
- banana pear
- Common lines
- banana, pear
- Common count
- 2
- Text a lines
- 5
Finding the overlap between two pasted lists should not require a spreadsheet formula, a shell pipeline, or a careful manual scan. This tool compares two multi-line texts and returns the unique lines that occur in both. Results follow the order of the first text, making the output predictable and easy to paste into another workflow. Exact comparison is used by default, so spaces, punctuation, and letter case remain meaningful. An optional case-insensitive mode can match entries such as Apple and apple while retaining the first text's original spelling.
How the common-line comparison works
The capability treats each input as a sequence of lines and computes a set intersection between those sequences. It first normalizes carriage-return line endings to newline characters, which means a Windows list and a Unix list can be compared without false differences caused only by file format. Every line from the second text becomes a lookup key.
The first text is then scanned from top to bottom, and a line is emitted when its key exists in the second set and has not already been emitted. This produces each shared line once, in the order of its first appearance in the first text. By default, comparison is exact: leading spaces, trailing spaces, punctuation, accents, and uppercase letters all affect equality.
Blank lines are legitimate values, so a blank line shared by both inputs appears once in the result.
Practical list-intersection workflows
Common-line matching is useful whenever two systems export one value per row and you need the overlap. A release engineer can compare package names from two deployments to see which dependencies are shared. An analyst can paste customer segments from separate reports and obtain the identifiers present in both without uploading a workbook or writing a join.
Content teams can compare keyword, tag, or URL lists while preserving the priority order of the first source. Teachers can intersect vocabulary lists, event organizers can compare attendance rosters, and operations teams can identify inventory codes included in two snapshots. For security-sensitive material, remember that the comparison is literal rather than semantic: it does not decide whether two similar domains, names, or paths refer to the same thing.
It also does not trim whitespace, perform Unicode normalization, sort results, count duplicate occurrences, or calculate lines found only on one side. Those choices are deliberate because silent cleanup can merge values that should remain distinct. Prepare or normalize your lists first when your workflow calls for those transformations, then use this capability for the transparent intersection step.
The array output works well in applications, while the joined text is convenient for immediate copy and paste.
What you can do with it
Compare exported identifier lists
Find account IDs, product codes, or record keys that occur in exports from two systems while preserving the first export's order.
Intersect tags and keywords
Identify shared tags, search terms, or categories, with optional case-insensitive comparison for human-readable labels.
Check deployment inventories
Compare one-item-per-line package or service inventories and return the unique entries installed in both environments.
FAQ
Are repeated common lines returned more than once?
No. Each matching line is returned once, even if it appears multiple times in either input.
Does the tool ignore spaces around a line?
No. Whitespace is significant, so a line with trailing spaces differs from the same visible text without those spaces.
In what order are the shared lines listed?
In the order they first appear in the first text, never alphabetically and never in the order of the second list. That is why the example gives banana before pear, even though pear comes first in the second list.
Can lines that differ only in capitals still match?
Yes, turn on the option to ignore case and Apple will match apple. The result keeps the spelling used in the first text, so your original capitalization is not lost.
Does it also show lines found in only one list?
No. It returns only the overlap, the lines present in both texts. It does not list what is missing from either side, sort the result or count how often a line repeats.
Related tools
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/common-lines \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text_a":"apple\nbanana\ncherry\npear","text_b":"banana\npear\norange"}'const res = await fetch("https://api.kit.forhosting.com/str/common-lines", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text_a": "apple\nbanana\ncherry\npear",
"text_b": "banana\npear\norange"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/str/common-lines",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text_a": "apple\nbanana\ncherry\npear",
"text_b": "banana\npear\norange"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/str/common-lines", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text_a":"apple\\nbanana\\ncherry\\npear","text_b":"banana\\npear\\norange"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text_a":"apple\nbanana\ncherry\npear","text_b":"banana\npear\norange"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/str/common-lines", 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_a": "apple\nbanana\ncherry\npear",
"text_b": "banana\npear\norange"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "str.common_lines",
"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_per_text | 65536 |
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. |
Technical notes
Line endings from Windows, Unix, and older Mac files are normalized before comparison, and the same deterministic function powers both browser and API execution.
How the common-line comparison works
If ignore_case is enabled, lowercase forms are used only as comparison keys; the returned value keeps the original spelling from the first text. The algorithm performs bounded linear scans and uses no network, model, random value, or clock, making repeated requests with identical inputs return identical JSON.
Choosing inputs and reading the result
Each field accepts up to 65,536 characters. Exact mode is appropriate for case-sensitive usernames, configuration keys, hashes, paths, and codes where even a small character difference matters. Duplicates within either source never cause duplicate output because this is set-style intersection rather than occurrence pairing. Send text_a and text_b as non-empty strings containing the lists or text blocks you want to compare. Use newline characters between items, and leave ignore_case omitted or false when identifiers must match exactly. Turn ignore_case on for human-readable categories, product names, tags, or email-domain lists where capitalization is not significant. The response includes text, a newline-joined version ready to copy; common_lines, the same values as a JSON array; and common_count, the number of unique shared lines. It also reports the line counts of both normalized inputs and the resolved ignore_case option. The order comes from text_a, not from text_b and not from an alphabetical sort. If either required value is missing, is not a string, is empty, or exceeds its limit, the request returns invalid_input. Successful API processing costs $0.002 per item, while the browser experience runs the same pure comparison logic locally.
Technical questions
Which input determines the result order?
The first text determines order. Shared lines appear according to their first occurrence in text_a.
What does ignore_case change?
It compares lowercase keys but keeps the original spelling from text_a in the returned result.
How much does the API request cost?
A successful API request costs $0.002 per item. The browser version uses the same deterministic logic locally.