Skip to main content
Developer Portal

Category Finder API

Send product titles, get back the Google product category for each: the numeric ID, the full path and a confidence level. Calling it from your own code takes a WISEPIM API key, which comes with the Pro plan.

Want to try it first? The free browser tool runs the same endpoint for up to 5 products a day, no account or key needed.

Open the Product Category Finder

Endpoint

POST https://wisepim.com/api/public/category-finder

Authentication

Send a WISEPIM project API key as a Bearer token. Keys start with wpk_. Any live project key works; the endpoint needs no particular scope. API keys are part of the Pro plan.

Authorization: Bearer wpk_...

Without a key, every request needs a Cloudflare Turnstile token, which only the browser tool can produce. Scripts and servers need a Pro plan API key. A Bearer value that is not a live key gets a 401; it does not fall back to the anonymous tier.

API keys come with the Pro plan and belong to a WISEPIM project. Project owners and admins create them in Settings > API access: name the key, keep the default read permissions, and copy it right away, because WISEPIM shows the full key only once. Keep the key on your server: never ship it in browser code.

Limits

Category Finder API limits per tier
LimitWith a Pro plan API keyWithout a key (browser)
Products per request205
Products per day (UTC)100 per key5 per visitor
Requests per minuteThe key's own limit (600 by default)Not applicable
  • Limits count products, not requests: a request with 3 titles uses 3.
  • Daily counters reset at 00:00 UTC.
  • All callers, with or without a key, also share one daily Category Finder budget. When it is spent, every caller gets daily_budget_exhausted until midnight UTC.
  • A request that fails with 503 is not counted.

Request

A JSON body with Content-Type: application/json. Unknown keys are ignored.

Request body fields
FieldTypeDescription
itemsarray, required1 to 20 products with an API key (1 to 5 without one). More is a 400.
items[].titlestring, requiredThe product title, 3 to 300 characters after trimming.
items[].descriptionstring, optionalUp to 2,000 characters. Helps when the title is short or ambiguous.
localestring, optionalOne of "en", "nl", "de", "fr", "es". Default "en". Sets the language of reason, and of google.path for "nl".
turnstileTokenstring, browser onlyA Cloudflare Turnstile token. Required without an API key; ignored when a valid key is sent.
curl
curl -X POST https://wisepim.com/api/public/category-finder \
  -H "Authorization: Bearer $WISEPIM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "locale": "en",
    "items": [
      { "title": "Velvet 3-seater sofa, emerald green" },
      { "title": "Stainless steel French press, 1 litre",
        "description": "Double-walled coffee maker with a 4-level filter." }
    ]
  }'
JavaScript (fetch)
const response = await fetch("https://wisepim.com/api/public/category-finder", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.WISEPIM_API_KEY}`,
  },
  body: JSON.stringify({
    locale: "en",
    items: [{ title: "Velvet 3-seater sofa, emerald green" }],
  }),
});

if (response.status === 429) {
  // Every 429 carries Retry-After: wait that many seconds before retrying.
  const waitSeconds = Number(response.headers.get("Retry-After") ?? "60");
  throw new Error(`Rate limited, retry in ${waitSeconds}s`);
}

const data = await response.json();
if (!response.ok) throw new Error(data.error);

const matches = data.results.map((result) => ({
  input: result.input,
  // google is null when no category could be confirmed in Google's file.
  googleId: result.google?.id ?? null,
  confidence: result.confidence,
}));

Response

200 OK with one result per product. The reasons below are illustrative; the IDs and paths are real entries from Google's file.

200 response
{
  "results": [
    {
      "input": "Velvet 3-seater sofa, emerald green",
      "google": { "id": "460", "path": "Furniture > Sofas" },
      "gpc": null,
      "confidence": "high",
      "reason": "A three-seat upholstered sofa fits Sofas directly."
    },
    {
      "input": "Stainless steel French press, 1 litre",
      "google": {
        "id": "1557",
        "path": "Home & Garden > Kitchen & Dining > Kitchen Appliances > Coffee Makers & Espresso Machines > French Presses"
      },
      "gpc": null,
      "confidence": "high",
      "reason": "The title names a French press coffee maker."
    }
  ],
  "remaining": { "today": 98 },
  "taxonomyVersion": "google-2021-09-21"
}
Response body fields
FieldTypeDescription
resultsarrayOne result per item, in the order you sent them.
results[].inputstringThe title you sent, trimmed.
results[].googleobject or nullid (numeric string) and path from Google's product taxonomy. Both are checked against Google's published file; when the match cannot be confirmed this is null, never an invented ID.
results[].gpcnullReserved for a GS1 GPC brick. Always null today: no verified GPC dataset backs it yet.
results[].confidencestring"high", "medium" or "low". Always "low" when google is null.
results[].reasonstringOne sentence in the request locale explaining the match, or why there is none.
remaining.todayintegerProducts you can still classify today (UTC).
taxonomyVersionstringThe Google taxonomy file the IDs were checked against, for example "google-2021-09-21".

Errors

Errors are JSON with an error code. Optional keys are left out, never sent as null. Every 429 sends a Retry-After header (seconds) and a matching retryAfterSeconds.

Category Finder API error codes
StatuserrorMeaning
400invalid_requestMalformed JSON, a field out of range, or too many items for your tier.
401invalid_api_keyThe Bearer value is not a live WISEPIM API key (unknown, revoked or expired).
403turnstile_failedNo API key and the Turnstile token is missing or invalid.
429rate_limitedYour daily product limit, or your key's per-minute request limit, is used up. The daily refusal also carries remaining.today.
429daily_budget_exhaustedThe Category Finder's shared daily budget is spent for everyone. It resets at 00:00 UTC.
503unavailableThe AI provider or a dependency failed. The products are not counted against your limit; retry later.
429 example
HTTP/1.1 429 Too Many Requests
Retry-After: 3600

{ "error": "rate_limited", "retryAfterSeconds": 3600, "remaining": { "today": 0 } }

Google taxonomy version

IDs and paths come from the Google product taxonomy files with IDs that Google publishes, version 2021-09-21. The version is returned in taxonomyVersion on every response, so you can tell when it changes.

Results are checked against Google's English (en-US) and Dutch (nl-NL) files. With "nl" you get the Dutch path; with "en", "de", "fr" or "es" you get the English path. The ID is the same in every language, so send the ID in your google_product_category feed attribute.

Titles are used to answer the request. The finder's usage analytics record counts, never titles, and your IP address is hashed for the daily limit, never stored as is.

Something not documented?

Tell us what you are building and we will point you to the right endpoint, or add it.

Ask us