Match products
A supplier feed calls it 'Wireless Mouse Blk 2.4G' and your catalogue calls the same item 'Black Wireless Optical Mouse' — no shared ID, no exact string match, just two different people describing the same product. This endpoint reads both sides and figures out which rows are actually the same item, so catalogue merges stop being a week of spreadsheet cross-referencing.
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, plainly
Anyone who has merged a supplier feed into an internal catalogue knows the pain: SKUs don't match, product names are phrased differently by every vendor, and a simple VLOOKUP only catches the handful of items that happen to be spelled identically. Multiply that by a distributor with ten thousand SKUs and a retailer onboarding three new suppliers a quarter, and manual reconciliation becomes a recurring, tedious, error-prone job that someone dreads every time a new price list arrives.
What matching actually does here
You submit your catalogue and the supplier's item list, and the task runs asynchronously, returning a task_id immediately. It compares each supplier item against your catalogue by meaning and attributes rather than requiring an exact string match, and returns the best matches with a task_id-linked result delivered via signed webhook or a 24-hour signed link, so you get a mapping table you can review or auto-apply.
Why exact matching was never enough
Barcode standards like UPC and EAN solved product identification decades ago, in theory, but in practice suppliers routinely omit them, use internal codes instead, or list variants under slightly different descriptions. Fuzzy matching exists precisely because the real world doesn't hand you clean, shared identifiers — it hands you 'Blk' instead of 'Black' and abbreviated brand names, and the matching has to read past that noise to the product underneath.
Fitting into a catalogue pipeline
This is typically run whenever a new supplier feed lands, whether that's a one-time onboarding of a new vendor's full catalogue or a recurring weekly price-and-stock update. Because pricing is per request plus per item, matching a batch of a thousand supplier SKUs against your catalogue has a computable cost, and failed matching tasks are retried automatically at no charge, keeping a flaky upload from turning into an unplanned expense.
Where a human still belongs
Matching returns its best candidates, typically with enough signal to auto-apply the high-confidence ones and flag the ambiguous ones for a quick manual check — a genuinely new product with no catalogue counterpart should stay unmatched rather than get force-fit into the wrong entry. The endpoint is built to shrink the reconciliation job to a short review list, not to remove human judgment from edge cases entirely.
What you can do with it
New supplier onboarding
A retailer receives a new distributor's full price list and matches its two thousand items against the existing catalogue in one batch instead of manually cross-referencing SKUs by hand.
Weekly price feed updates
A wholesaler's automated pricing pipeline matches each incoming supplier feed to internal SKUs before updating prices, so a mismatch never silently overwrites the wrong product.
Multi-supplier price comparison
A buyer matches the same product line across three different suppliers' catalogues to compare pricing on equivalent items, even though each supplier names them differently.
Catalogue deduplication
A marketplace merging two acquired catalogues uses matching to find overlapping products listed under different names, flagging duplicates before the combined catalogue goes live.
FAQ
How does the product matching API work without shared SKUs?
It compares product names, descriptions and attributes for semantic similarity, so it can match items even when suppliers use completely different codes or wording.
Is this exact matching or fuzzy matching?
It's fuzzy matching — it's built specifically to handle inconsistent naming, abbreviations and missing identifiers, not just identical strings.
Is there a free tier for search.match_products?
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 does bulk product matching cost?
It's $0.002 per request plus $0.002 per item, so matching a large supplier feed has a predictable, computable cost before you run it.
Does it guarantee every item gets matched?
No, and that's intentional — genuinely new products with no catalogue counterpart are correctly left unmatched rather than forced into the wrong entry.
Can I match against thousands of supplier SKUs at once?
Yes, it's designed for bulk use — submit a full supplier feed in one batch and pricing scales per item matched.
How do I receive the matching results?
The call returns a task_id immediately, and the mapping is delivered via signed webhook or a signed link valid for 24 hours.
What happens if a matching task fails?
It's retried automatically up to three times, and you're never charged for a task that ultimately fails.
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/match-products \
-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/search/match-products", {
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/search/match-products",
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/search/match-products", 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/search/match-products", 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": "search.match_products",
"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. |