Classify Products
curl --request POST \
--url https://api.numeralhq.com/tax/products/classify \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"products": [
{
"reference_product_id": "sku-shirt-001",
"reference_product_name": "Cotton crew neck t-shirt",
"reference_product_description": "Unisex short-sleeve shirt, 100% cotton"
}
],
"create_products": false
}
'import requests
url = "https://api.numeralhq.com/tax/products/classify"
payload = {
"products": [
{
"reference_product_id": "sku-shirt-001",
"reference_product_name": "Cotton crew neck t-shirt",
"reference_product_description": "Unisex short-sleeve shirt, 100% cotton"
}
],
"create_products": False
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
products: [
{
reference_product_id: 'sku-shirt-001',
reference_product_name: 'Cotton crew neck t-shirt',
reference_product_description: 'Unisex short-sleeve shirt, 100% cotton'
}
],
create_products: false
})
};
fetch('https://api.numeralhq.com/tax/products/classify', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.numeralhq.com/tax/products/classify",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'products' => [
[
'reference_product_id' => 'sku-shirt-001',
'reference_product_name' => 'Cotton crew neck t-shirt',
'reference_product_description' => 'Unisex short-sleeve shirt, 100% cotton'
]
],
'create_products' => false
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.numeralhq.com/tax/products/classify"
payload := strings.NewReader("{\n \"products\": [\n {\n \"reference_product_id\": \"sku-shirt-001\",\n \"reference_product_name\": \"Cotton crew neck t-shirt\",\n \"reference_product_description\": \"Unisex short-sleeve shirt, 100% cotton\"\n }\n ],\n \"create_products\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.numeralhq.com/tax/products/classify")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"products\": [\n {\n \"reference_product_id\": \"sku-shirt-001\",\n \"reference_product_name\": \"Cotton crew neck t-shirt\",\n \"reference_product_description\": \"Unisex short-sleeve shirt, 100% cotton\"\n }\n ],\n \"create_products\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.numeralhq.com/tax/products/classify")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"products\": [\n {\n \"reference_product_id\": \"sku-shirt-001\",\n \"reference_product_name\": \"Cotton crew neck t-shirt\",\n \"reference_product_description\": \"Unisex short-sleeve shirt, 100% cotton\"\n }\n ],\n \"create_products\": false\n}"
response = http.request(request)
puts response.read_body{
"object": "list",
"create_products": false,
"product_categories": [
{
"reference_product_id": "sku-shirt-001",
"status": "classified",
"product_category": "CLOTHING_GENERAL",
"confidence_score": 0.94,
"product_creation_status": "not_requested"
}
],
"testmode": true
}{
"code": 400,
"type": "MISSING_FIELD",
"message": "Required field 'address_country' is missing"
}{
"code": 400,
"type": "MISSING_FIELD",
"message": "Required field 'address_country' is missing"
}{
"code": 400,
"type": "MISSING_FIELD",
"message": "Required field 'address_country' is missing"
}{
"code": 400,
"type": "MISSING_FIELD",
"message": "Required field 'address_country' is missing"
}Classify Products
Suggest a Numeral tax category for up to 20 products from their names and descriptions, using the AI provider keys stored for your account. Optionally create the classified products in one call. Available on API version 2026-01-01 and later.
POST
/
tax
/
products
/
classify
Classify Products
curl --request POST \
--url https://api.numeralhq.com/tax/products/classify \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"products": [
{
"reference_product_id": "sku-shirt-001",
"reference_product_name": "Cotton crew neck t-shirt",
"reference_product_description": "Unisex short-sleeve shirt, 100% cotton"
}
],
"create_products": false
}
'import requests
url = "https://api.numeralhq.com/tax/products/classify"
payload = {
"products": [
{
"reference_product_id": "sku-shirt-001",
"reference_product_name": "Cotton crew neck t-shirt",
"reference_product_description": "Unisex short-sleeve shirt, 100% cotton"
}
],
"create_products": False
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
products: [
{
reference_product_id: 'sku-shirt-001',
reference_product_name: 'Cotton crew neck t-shirt',
reference_product_description: 'Unisex short-sleeve shirt, 100% cotton'
}
],
create_products: false
})
};
fetch('https://api.numeralhq.com/tax/products/classify', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.numeralhq.com/tax/products/classify",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'products' => [
[
'reference_product_id' => 'sku-shirt-001',
'reference_product_name' => 'Cotton crew neck t-shirt',
'reference_product_description' => 'Unisex short-sleeve shirt, 100% cotton'
]
],
'create_products' => false
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.numeralhq.com/tax/products/classify"
payload := strings.NewReader("{\n \"products\": [\n {\n \"reference_product_id\": \"sku-shirt-001\",\n \"reference_product_name\": \"Cotton crew neck t-shirt\",\n \"reference_product_description\": \"Unisex short-sleeve shirt, 100% cotton\"\n }\n ],\n \"create_products\": false\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.numeralhq.com/tax/products/classify")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"products\": [\n {\n \"reference_product_id\": \"sku-shirt-001\",\n \"reference_product_name\": \"Cotton crew neck t-shirt\",\n \"reference_product_description\": \"Unisex short-sleeve shirt, 100% cotton\"\n }\n ],\n \"create_products\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.numeralhq.com/tax/products/classify")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"products\": [\n {\n \"reference_product_id\": \"sku-shirt-001\",\n \"reference_product_name\": \"Cotton crew neck t-shirt\",\n \"reference_product_description\": \"Unisex short-sleeve shirt, 100% cotton\"\n }\n ],\n \"create_products\": false\n}"
response = http.request(request)
puts response.read_body{
"object": "list",
"create_products": false,
"product_categories": [
{
"reference_product_id": "sku-shirt-001",
"status": "classified",
"product_category": "CLOTHING_GENERAL",
"confidence_score": 0.94,
"product_creation_status": "not_requested"
}
],
"testmode": true
}{
"code": 400,
"type": "MISSING_FIELD",
"message": "Required field 'address_country' is missing"
}{
"code": 400,
"type": "MISSING_FIELD",
"message": "Required field 'address_country' is missing"
}{
"code": 400,
"type": "MISSING_FIELD",
"message": "Required field 'address_country' is missing"
}{
"code": 400,
"type": "MISSING_FIELD",
"message": "Required field 'address_country' is missing"
}Classify Products Endpoint (2026-01-01)
Suggest a tax category for each product from its name and description, and optionally create the products in the same call. Every suggested category is a code from the product categories list, so the result can go straight intoPOST /tax/products or a calculation line item.
Product classification is in limited availability. Accounts that are not enabled receive a
404 ENDPOINT_NOT_FOUND from this endpoint. Contact your Numeral representative to enable it.Remember to include the
X-API-Version: 2026-01-01 header in your request to use this API version. This endpoint requires version 2026-01-01 or later.Before you call it
Classification runs on your own AI provider keys, not Numeral’s. Store them in the dashboard under Developers > Product Classification AI API Keys and Settings before calling this endpoint. Test keys (sk_test_...) use the keys saved for the test environment and production keys use the production ones. A single OpenAI key is enough; Gemini and Anthropic keys are also supported. See the product classification guide for details.
How to read a result
Each entry inproduct_categories has a status:
status | product_category | confidence_score | Meaning |
|---|---|---|---|
classified | A category code | 0 to 1 | A category was chosen. Review low scores before relying on them. |
fallback | GENERAL_MERCHANDISE | null | No category could be chosen. Treat the suggestion as a placeholder. |
failed | null | null | This product could not be processed. Retry it in a later batch. |
create_products: true, product_creation_status reports what happened to each product record. Only classified products are created; fallback and failed products report not_classified. A product that already exists under the same reference_product_id is left unchanged and reports already_exists.
Limits
- Up to 20 products per request.
- One classification request in flight per account. A second concurrent request gets
429with aRetry-Afterheader. - A full batch can take tens of seconds, so allow at least 60 seconds for the response.
Errors
| HTTP | type | When |
|---|---|---|
| 400 | PRODUCT_CLASSIFICATION_CREDENTIALS_MISSING | No AI provider keys are stored for this environment. |
| 400 | PRODUCT_CLASSIFICATION_CREDENTIALS_INVALID | The stored keys were rejected by the provider. Re-enter them in the dashboard. |
| 404 | ENDPOINT_NOT_FOUND | Product classification is not enabled for this account. |
| 429 | PRODUCT_CLASSIFICATION_CAPACITY_EXCEEDED | Another request is in progress for this account, or the service is at capacity. Retry after Retry-After seconds. |
| 429 | PRODUCT_CLASSIFICATION_PROVIDER_QUOTA | Your AI provider rate limited the request. Retry after Retry-After seconds. |
| 503 | PRODUCT_CLASSIFICATION_UNAVAILABLE | The service is temporarily unavailable, or every product in the batch failed. Retry with backoff. |
Authorizations
Body
application/json
When true, every product that classifies successfully is also created with the suggested category, as if sent to POST /tax/products. Products already registered under the same reference_product_id are left unchanged. reference_product_id must then be unique within the batch.
Example:
false
Response
One result per input product, in request order