Classify text overall tone from a custom word lexicon
In the sample request, three formal words, one urgent and one friendly give a formal tone with a confidence of 0.6, that is 3 of 5 matches. You write the word lists, so every result can be checked word by word. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Runs in your browser. Free, unlimited — your data never leaves this page.
This overall tone classifier measures a piece of writing against a lexicon that you control. Supply word lists for formal, casual, urgent, and friendly language, and the classifier counts whole-word matches without using a model or making a network request. It returns the dominant tone, per-tone counts, the words that matched, a confidence share, and whether a requested default was used. Because the same input and lexicon always produce the same result, the output is suitable for repeatable editorial checks, routing rules, and tests.
Build a lexicon that represents your writing rules
A useful classification begins with a lexicon designed for the material being reviewed. Add words such as “therefore,” “regarding,” or “hereby” to formal when they genuinely signal formality in your organization. Casual might contain “hey,” “cool,” or “yeah,” while urgent could include “immediately,” “deadline,” and “critical.”
Friendly language might be represented by “welcome,” “thanks,” and “glad.” Every category is explicit, so reviewers can inspect and revise the rule instead of guessing why a hidden model chose a label. Entries are matched as complete words without regard to letter case.
A word may appear in more than one category; when it does, each occurrence contributes to every category that contains it. Duplicate entries within one category are collapsed, preventing an accidental repeated lexicon item from multiplying its influence. Keep entries specific enough to indicate tone in your domain.
Common words with little tonal meaning can overwhelm a more informative but less frequent signal.
Understand the score and deterministic tie rule
The classifier tokenizes the supplied text and looks up every token in the four category lists. Repeated words in the text are counted repeatedly because repetition can strengthen the apparent tone. The result includes counts for formal, casual, urgent, and friendly, plus distinct matched words for each category, so the conclusion remains auditable.
The dominant tone is the category with the greatest match count. Confidence is that winning count divided by all category matches; it describes the share of observed indicators supporting the selected category, not a statistical probability about the author’s intent. If categories tie for the highest count, the stable priority is formal, then casual, then urgent, then friendly.
This fixed rule ensures identical output in automation and browser use, though callers can inspect the counts and apply a different tie policy downstream. Punctuation and capitalization do not affect ordinary matches. Lexicon entries must each be one word, so phrases are intentionally outside this capability’s contract.
For a phrase-level or semantic judgment, a model-based analyzer is more appropriate; this tool is designed for transparent word evidence.
Choose whether unmatched text should fail or use a default
An absence of matches is meaningful. By default, the classifier returns an invalid-input error when none of the supplied tone indicators appears in the text. That behavior prevents a workflow from silently labeling neutral or out-of-vocabulary writing as though evidence supported the decision.
A practical workflow might omit the default during lexicon development so missing vocabulary is visible, then add a default only after the dictionary has been evaluated on representative content. The function does not infer sarcasm, sentence structure, audience, or context. Its strength is reproducibility: the same bounded text and the same lexicon always yield the same counts, label, and fallback status, making changes easy to test before editorial rules are deployed.
What you can do with it
Route support messages
Flag messages dominated by urgent vocabulary for a faster queue while retaining the exact matched words as evidence.
Enforce a style guide
Check drafts against organization-specific formal and casual word lists before publication.
Test generated copy
Assert a repeatable tone label in automated tests without relying on variable model output.
FAQ
How much does one classification cost?
The browser version runs the same deterministic logic locally.
Are matches case-sensitive?
No. Text and lexicon entries are normalized to lowercase before whole-word matching.
What happens when two tones tie?
The stable priority is formal, casual, urgent, then friendly. The complete counts remain available if you prefer another downstream rule.
Can a word belong to multiple tone categories?
Yes. Each occurrence contributes once to every category containing that word, and the result lists it under each matching category.
Does it understand sarcasm or context?
No. It classifies only explicit word matches. That limitation is also what makes the result transparent and reproducible.
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/write/tone-word-classify \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Please review this document promptly. Your assistance is appreciated.","lexicon":{"formal":["please","document","appreciated"],"casual":["hey","cool","yeah"],"urgent":["promptly","immediately","urgent"],"friendly":["thanks","welcome","assistance"]}}'const res = await fetch("https://api.kit.forhosting.com/write/tone-word-classify", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "Please review this document promptly. Your assistance is appreciated.",
"lexicon": {
"formal": [
"please",
"document",
"appreciated"
],
"casual": [
"hey",
"cool",
"yeah"
],
"urgent": [
"promptly",
"immediately",
"urgent"
],
"friendly": [
"thanks",
"welcome",
"assistance"
]
}
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/write/tone-word-classify",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "Please review this document promptly. Your assistance is appreciated.",
"lexicon": {
"formal": [
"please",
"document",
"appreciated"
],
"casual": [
"hey",
"cool",
"yeah"
],
"urgent": [
"promptly",
"immediately",
"urgent"
],
"friendly": [
"thanks",
"welcome",
"assistance"
]
}
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/write/tone-word-classify", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"Please review this document promptly. Your assistance is appreciated.","lexicon":{"formal":["please","document","appreciated"],"casual":["hey","cool","yeah"],"urgent":["promptly","immediately","urgent"],"friendly":["thanks","welcome","assistance"]}}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"Please review this document promptly. Your assistance is appreciated.","lexicon":{"formal":["please","document","appreciated"],"casual":["hey","cool","yeah"],"urgent":["promptly","immediately","urgent"],"friendly":["thanks","welcome","assistance"]}}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/write/tone-word-classify", 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": "Please review this document promptly. Your assistance is appreciated.",
"lexicon": {
"formal": [
"please",
"document",
"appreciated"
],
"casual": [
"hey",
"cool",
"yeah"
],
"urgent": [
"promptly",
"immediately",
"urgent"
],
"friendly": [
"thanks",
"welcome",
"assistance"
]
}
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "write.tone_word_classify",
"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 | 100000 |
max_lexicon_words_per_tone | 1000 |
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
Build a lexicon that represents your writing rules
The endpoint accepts up to one thousand words per category, which is ample for focused editorial dictionaries while keeping classification bounded and predictable.
Choose whether unmatched text should fail or use a default
If your application needs every item routed, provide default_tone with one of the four supported labels. The result then identifies that label, reports zero counts and zero confidence, and sets used_default to true. This makes evidence-based classifications distinguishable from fallback routing without requiring a second request. Review matched_words when a classification surprises you: an overly broad term, a domain-specific use, or a word shared by multiple categories is often responsible.
Technical questions
What happens when there are no matches?
The request returns invalid_input unless default_tone is supplied. With a default, used_default is true and confidence is zero.