List unique words
"The quick brown fox jumps over the lazy dog. The fox is quick!" has 13 words but only 9 different ones, listed as they first appear: The, quick, brown, fox, jumps, over, lazy, dog, is. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Listing unique words is a small text utility with a clear product promise: take non-empty text, split it into words, drop duplicates, and return the remaining tokens in the order they first appeared. Writers use it to audit vocabulary, teachers use it to build word banks, and pipelines use it to build stable sets for tests without reimplementing tokenization in every language. This capability supports an optional case-insensitive mode so Hello and hello count as one entry while still preserving the first surface form you typed.
What unique words mean and how order of appearance is preserved
A unique-word list answers a search intent people type every day: list unique words from text, extract word list without duplicates, or get vocabulary in reading order. This capability defines a word as a contiguous run of Unicode letters or digits, optionally joined by an internal apostrophe or hyphen so contractions and hyphenated compounds stay one token. Everything else—spaces, commas, periods, emoji separators, and punctuation—is a boundary.
Scanning left to right, each token is kept only the first time its uniqueness key is seen; later repeats are dropped. When case-insensitive mode is on, the uniqueness key is the lowercased form, but the list stores the first surface spelling so a title-case first mention stays readable in UIs. When case-insensitive mode is off, Hello and hello are two separate entries if both appear.
Order is always first-seen, never alphabetical, because frequency tables and sorted dumps are a different product intent. Prefer this list-unique-words tool when you want a stable ordered vocabulary snapshot. Do not use it for ranked SEO keywords, stemming, stopword removal, or sentence splitting—those landings serve other queries.
What you can do with it
Vocabulary banks and flashcards
Teachers and learners paste a paragraph and get a clean ordered word list for flashcards, without manual duplicate cleanup.
Draft vocabulary audits
Writers check which unique words appear in a draft and whether case variants were accidentally mixed across headings and body copy.
FAQ
How do I list unique words from text?
The response words array lists each unique word in first-seen order, with count set to the array length.
Is the list unique words tool free?
The browser tool runs free on your device.
Does it remove stopwords or stem words?
No. It only tokenizes and drops exact (or case-folded) duplicates while preserving first-seen order. Ranking, stemming, and stopword lists are out of scope.
Do Hello and hello count as the same word?
Yes, by default. Case is ignored when spotting repeats, but the list keeps the spelling you used first, so a capitalized first mention stays readable. Turn that option off and Hello and hello appear as two entries.
Why is the list not in alphabetical order?
Because it keeps the order of your writing, with each word where it was first used and never sorted. That keeps the reading order for flashcards and vocabulary checks; sorting or ranking words by frequency is a different task.
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/extract-unique-words \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"The quick brown fox jumps over the lazy dog. The fox is quick!"}'const res = await fetch("https://api.kit.forhosting.com/str/extract-unique-words", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "The quick brown fox jumps over the lazy dog. The fox is quick!"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/str/extract-unique-words",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "The quick brown fox jumps over the lazy dog. The fox is quick!"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/str/extract-unique-words", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"The quick brown fox jumps over the lazy dog. The fox is quick!"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"The quick brown fox jumps over the lazy dog. The fox is quick!"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/str/extract-unique-words", 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": "The quick brown fox jumps over the lazy dog. The fox is quick!"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "str.extract_unique_words",
"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. |
Technical notes
Empty input fails with invalid input so callers never store zero-length payloads by accident.
Input contract, empty rejection, and reading the response fields
Whitespace-only text is allowed when it is not empty: it may yield zero tokens and an empty words array, which is a valid success shape. Because the algorithm is pure string logic, continuous-integration tests can pin the full object. Source text follows the same retention rules as other KIT parse items. Send a JSON body with the required string field text set to the non-empty sample you want analyzed. Aliases input, value, s, content, and body are accepted when older clients or form generators use those names. Missing text, a non-string value, or a zero-length string all raise invalid_input with a message that names the problem, and those failures are not charged on the prepaid API. Optional case_insensitive defaults to true; aliases ignore_case, ci, and casefold are accepted. On success the response includes words as the ordered unique list, count and unique_count as the same cardinality for SDK convenience, total_tokens as how many word tokens were seen before uniqueness, case_insensitive echoing the mode used, original echoing the exact input for UI tables, and method always extract_unique_words so downstream code can branch without sniffing shapes. Use words alone when a flashcard deck only needs the vocabulary set; use total_tokens next to count when you want a quick sense of how much repetition the source had. Keep payloads to ordinary document sizes suitable for a Worker isolate rather than multi-megabyte dumps; this product is a text utility, not a bulk warehouse.
API usage, pricing, workflows, and practical limits
Call the free in-browser list unique words tool for homework word banks, style audits, and quick vocabulary checks without writing a split-and-Set one-liner in every language. A typical workflow pastes a paragraph, lists unique words with case-insensitive mode, then compares against a reading-level list. Do not expect language detection, lemmatization, or multi-language stemming—those are separate problems with separate SEO terms. Authors shipping SDKs can document text as the only required field, show a success example with repeated words, and show an empty-string error example so integrators see both sides of the contract. Use the prepaid KIT API when a CMS, content pipeline, QA harness, tutoring app, or mobile client needs the same contract under balance at $0.002 per successful item; validation failures never consume balance after the engine rejects the request. Pair this endpoint with keyword-density or diff tools when the next step is ranking or comparing two drafts, but keep list unique words as its own landing so the extract-word-list intent is not mixed with SEO keyword extraction or PDF stats.
Integration use cases
CI fixtures for token pipelines
Build scripts pin unique-word golden outputs so tokenization and case-fold regressions fail continuous integration immediately.
Technical questions
Are Hello and hello treated as the same word?
By default yes: case_insensitive is true, so they share one entry and the first surface form is kept. Set case_insensitive to false to list case-distinct tokens separately.
What happens if text is empty?
The request fails with invalid_input. Empty input is not allowed; validation failures are not charged on the prepaid API.