๐Ÿ—‚๏ธ Product Taxonomy Classifier - Deep Category Trees avatar

๐Ÿ—‚๏ธ Product Taxonomy Classifier - Deep Category Trees

Pricing

Pay per event

Go to Apify Store
๐Ÿ—‚๏ธ Product Taxonomy Classifier - Deep Category Trees

๐Ÿ—‚๏ธ Product Taxonomy Classifier - Deep Category Trees

โšก Classify thousands of products into a deep, multi-level category tree with no LLM. โœ… Walks the taxonomy level by level so every path is internally consistent, scores with TF-IDF-ish cosine + token overlap + trigram similarity, and routes low-confidence items to a review queue instead of.

Pricing

Pay per event

Rating

0.0

(0)

Developer

mohamed alaya

mohamed alaya

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

7 days ago

Last modified

Categories

Share

Product Taxonomy Classifier

Classify thousands of products into a deep, multi-level category tree โ€” deterministically, with no LLM and no embeddings model. This is the actor behind "sort our catalog into the category tree the storefront/marketplace actually uses" without paying per-token for an LLM call on every SKU.

What it actually does

1. Hierarchy, done properly. The moat is not "guess a category" โ€” it's guessing correctly, one level at a time. Level 1 is scored only against the taxonomy's root categories. Once a root is accepted, level 2 is scored only against that root's own children โ€” a "Headphones" leaf under "Automotive" can never be reached even if its keywords happen to overlap, because it is never a candidate once "Electronics" wasn't the level-1 pick. This repeats down the tree, so every emitted path is walked edge by edge and is always internally consistent.

2. Two taxonomy input shapes, freely mixed.

["Electronics > Audio > Headphones", "Electronics > Audio > Speakers"]

or a nested tree:

[{ "name": "Electronics", "children": [
{ "name": "Audio", "children": [
{ "name": "Headphones", "keywords": ["earbuds", "anc", "over-ear"] }
] }
] }]

3. Scoring blends three dependency-free signals, computed per level against the current candidate set only (see src/classify.js):

SignalWhat it catches
TF-IDF-ish cosineTerm weighting where IDF is built from the sibling categories competing at that level, so the terms that matter are the ones that discriminate between the current candidates
Token overlapHow much of a category's own vocabulary (name + keywords + hints) shows up in the product text
Character-trigram DiceTypo/plural/word-order tolerance that pure token matching misses

4. Confidence per level, not just per product. Confidence blends the winning score's raw strength with its separation from the runner-up โ€” a high score that's barely ahead of the next category is exactly the ambiguous case the review queue exists for.

5. A review queue, not a forced guess. The first level where nothing clears reviewBelow stops the walk there. The product is queued for review with the partial path reached, the reason, and the top-3 candidates that were considered โ€” never silently mis-filed.

6. Optional keyword hints. Pass extra keywords per category (by full path or bare name) to boost recall for categories whose name alone is too generic ("Boots" vs. hiking/rain/work boots).

7. Reporting. Per-category counts (type: "categoryCount") and a SUMMARY with the review rate, taxonomy shape, and top categories used.

Input

{
"products": [{ "title": "Sony WH-1000XM5 Headphones", "description": "...", "tags": ["audio"] }],
"taxonomy": ["Electronics > Audio > Headphones", "Electronics > Audio > Speakers"],
"keywordHints": { "Electronics > Audio > Headphones": ["earbuds", "anc"] },
"reviewBelow": 55,
"tfidfWeight": 50, "overlapWeight": 30, "trigramWeight": 20
}

reviewBelow and the three weights are 0โ€“100 integers (Apify input schemas have no float type); weights are normalized against each other, so only their ratio matters.

Output

type: "classified" rows carry the full path array, fullPath string, per-level levelConfidences, and an overall confidence (the weakest level in the chain โ€” a deep path is only as trustworthy as its worst step). type: "review" rows carry the reason and best-guess candidates. type: "categoryCount" rows summarize where products landed.

Honest limitations

  • Lexical, not semantic. There is no embeddings model here โ€” matching is TF-IDF/token/trigram based. "Footwear" and "Shoes" are related to a human but share almost no characters or tokens; give the taxonomy explicit keywords or keywordHints to bridge synonyms the algorithm can't infer on its own. This is a deliberate trade-off for a fast, deterministic, LLM-free actor.
  • Very short/sparse product text (a bare SKU with no description or tags) has little for any lexical method to work with and will often land in review rather than being force-fitted.
  • Confidence is a relative signal (how well the winner beat the field), not a calibrated probability โ€” tune reviewBelow against your own precision/recall needs.
  • Capped at 200,000 products per run.