Normalize product data
The same item arrives from three suppliers as 'iPhone 13 128GB Blk', 'Apple iPhone-13 128 GB Black' and 'IPHONE13 BLACK 128G', and your catalog ends up with three listings for one product. This endpoint reads inconsistent supplier naming and returns a single clean, canonical product name, so deduplication, search and price comparison all work against one identity instead of three near-matches.
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.
Why supplier names never agree
Every supplier writes product names for their own internal system, not for your storefront — abbreviations, inconsistent capitalization, model numbers jammed against colour codes, and units written three different ways all creep in over years of catalog entry by different people. Multiply that by a dozen suppliers feeding one catalog and you get a name-matching problem that spreadsheets and fuzzy string matching solve badly, which is exactly the gap product name normalization is built to close.
How the request and response work
POST an array of raw product name strings to /text/product-normalize, optionally with brand or category hints if the name alone is ambiguous. The task runs async — a task_id comes back immediately, and the normalized name for each entry lands at your signed webhook or a signed link valid for 24 hours, formatted consistently: proper capitalization, standard unit notation, brand and model separated cleanly, so 'iPhone13 BLK 128G' and 'Apple iPhone-13 128 GB Black' both resolve to the same canonical string.
The logic behind cleaning a name
It parses out the underlying entity — brand, model, key specs like storage or colour — rather than just reformatting capitalization, which is why it can recognize that '128G' and '128GB' describe the same storage and that 'Blk' is shorthand for 'Black' even without an explicit mapping table you maintain yourself. That's the same mental parsing a merchandiser does instinctively when they see a messy import file and immediately know which rows describe the same physical product.
Where it saves the most time
Catalog deduplication projects are the clearest win: normalize every raw name first, then group by the canonical output to collapse duplicate listings that manual review would take days to spot. It also pays off upstream, in feed ingestion — normalize supplier names on the way in, before they ever touch your product database, so duplicates never accumulate in the first place instead of needing periodic cleanup sprints.
What stays a human decision
Normalization cleans the name string; it doesn't decide that two visually different SKUs are secretly the same product (a 128GB and a 256GB variant, say, get correctly normalized as two distinct canonical names, not merged) — that's a deliberate boundary, because merging genuinely different SKUs would corrupt inventory counts. Treat it as the first, largest pass on a cleanup job, with a smaller manual review left for the genuinely ambiguous entries.
What you can do with it
Multi-supplier catalog deduplication
Normalize product names from every supplier feed and group by the canonical output to collapse duplicate listings automatically.
Price comparison tools
Match the same product across retailers by comparing normalized names instead of maintaining a brittle manual mapping table.
Marketplace listing cleanup
Standardize thousands of seller-submitted product titles into a consistent house style before they go live.
Search relevance improvements
Feed normalized names into your search index so queries for 'iphone 13 black' match listings regardless of how the supplier originally typed the title.
FAQ
What does the product name normalization API actually change?
It cleans capitalization, standardizes units and abbreviations, and separates brand, model and key specs into a consistent canonical name for each product.
Can it tell two different variants apart, like 128GB vs 256GB?
Yes, it normalizes each variant's name correctly without merging genuinely different SKUs into one, since that distinction has to stay accurate for inventory.
Is there a free trial?
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.
How much does normalizing a large batch of names cost?
$0.003 per request plus $0.0135 per item, so normalizing thousands of names has a fixed cost you can calculate before sending the batch.
How do I retrieve normalized names?
The task runs asynchronously: you get a task_id immediately, and results arrive via your signed webhook or a signed link valid for 24 hours.
Does this replace deduplication logic entirely?
It does the heavy lifting of cleaning names so you can group by the canonical output, but the actual dedup step (matching on that output) still runs in your own pipeline.
Can I pass hints like brand or category to improve results?
Yes, optional hints help disambiguate short or unclear raw names, and they're recommended when supplier names are especially terse.
Can I normalize thousands of product names in one request?
Yes, bulk arrays are the expected use case, with pricing scaling per item and no arbitrary limit on batch size.
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/text/product-normalize \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"items":["valor-1","valor-2"]}'const res = await fetch("https://api.kit.forhosting.com/text/product-normalize", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"items": [
"valor-1",
"valor-2"
]
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/text/product-normalize",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"items": [
"valor-1",
"valor-2"
]
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/text/product-normalize", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"items":["valor-1","valor-2"]}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"items":["valor-1","valor-2"]}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/text/product-normalize", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"items": [
"valor-1",
"valor-2"
]
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "text.product_normalize",
"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_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. |
422 | task_failed | The task failed after 3 retries. You are never charged for it. |