Cluster texts
Ten thousand support tickets, reviews or survey answers hide maybe a dozen real themes, but nobody has time to read all ten thousand to find them. This endpoint groups your rows by meaning and returns the clusters, so the shape of a pile of unstructured text becomes visible in minutes instead of a week of manual tagging.
Run it online
Run this on our servers with your account. Free tools run in your browser; this one bills your KIT balance per the price above.
The problem this solves
Free-text data — reviews, tickets, open survey questions, chat transcripts — is rich but shapeless. Traditional analytics tools need a category column that does not exist yet, and building one by hand does not scale past a few hundred rows before it becomes a full-time job for someone. Clustering skips the labeling step entirely: it reads the text and finds the natural groupings on its own, letting an analyst start from structure instead of from a blank spreadsheet.
What you send and what comes back
You submit a batch of text rows — a column of ticket bodies, product reviews, survey responses, whatever the dataset is — and the task runs asynchronously, returning a task_id right away. The result, delivered by signed webhook or a 24-hour signed link, is each row assigned to a cluster along with the groups themselves, so you can immediately see how many themes exist and how large each one is, without predefining the number of categories beforehand.
Why unsupervised grouping works here
Clustering is one of the oldest ideas in data analysis, going back to techniques like k-means from the 1960s, and the reason it still matters is simple: it does not require anyone to have decided the categories in advance. Instead of forcing new data into old buckets, the algorithm looks at what is semantically close to what and lets the groups emerge from the text itself. That matters for anything that evolves — new complaint types, new product feedback themes, new slang — because the clusters reflect what people are actually saying this month, not last year's taxonomy.
How it plugs into a workflow
Most teams run this on a recurring basis: a weekly batch of new support tickets, a monthly dump of app store reviews, a fresh export after every customer survey closes. Because pricing is per request plus per 1000 rows, the cost of clustering a growing dataset scales with volume in a way that's easy to budget, and a failed batch costs nothing since failed tasks are retried automatically and never billed.
What it is not
This groups texts by theme; it does not name the themes for you or explain why a row landed where it did beyond the grouping itself, and it will not merge with a rigid predefined taxonomy unless you build that mapping afterward. Think of it as the fast first pass that turns an unreadable pile of text into a short list of groups worth a human's attention.
What you can do with it
Support ticket triage
A helpdesk team clusters last week's tickets automatically and discovers a spike in a single theme — a shipping delay complaint — before it shows up as a trend anywhere else.
App review analysis
A product team runs clustering on a month of app store reviews and gets a handful of real themes instead of reading a thousand one-line comments individually.
Open-ended survey responses
A researcher clusters the free-text answers to 'what would you change?' from a customer survey, turning a wall of text into a handful of ranked themes for the next planning meeting.
Content deduplication prep
A publisher clusters archived articles by topic before a site redesign, using the groups to decide which old content to merge, redirect or retire.
FAQ
How does the text clustering API decide the groups?
It reads the meaning of each text row and groups rows that are semantically similar, without needing predefined categories or labels.
How many clusters will I get?
The number of clusters reflects the natural structure of your data rather than a number you must guess in advance, so it varies by dataset.
Is there a free tier for search.cluster?
There's no free tier — free tiers get abused and slow everyone down. Access runs on a prepaid ForHosting KIT balance: top up from $10.00 (it never expires) and each request is charged at its published price, so a call with no balance returns HTTP 402. No subscription, no tokens, no invented credits, and a failed task is never charged.
What's the pricing for clustering a large dataset?
It's $0.002 per request plus $0.002 per 1000 rows, so the cost of a bulk job is easy to estimate before running it.
What formats of text can I cluster?
Any plain text rows — reviews, tickets, comments, survey answers — submitted as a batch; the endpoint groups by meaning regardless of the original source.
Does it label or name each cluster?
It groups rows together; naming or interpreting each theme is left to you, since that judgment depends on your specific data and context.
How do I retrieve clustering results?
The call returns a task_id immediately, and results are delivered via signed webhook or a signed link valid for 24 hours.
Can I cluster bulk datasets in one call?
Yes, it's built for bulk use — submit thousands of rows in a single batch and pricing scales per 1000 rows processed, not per row.
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, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/search/cluster \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"…"}'const res = await fetch("https://api.kit.forhosting.com/search/cluster", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "…"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/search/cluster",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "…"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/search/cluster", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"…"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"…"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/search/cluster", 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": "…"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "search.cluster",
"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_chunks | 10000 |
max_tokens | 20000 |
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. |