淘宝类目详情接口开发文档
淘宝商品类目体系是电商平台最核心的基础数据结构之一,它决定了商品的展示方式、属性规则、搜索推荐逻辑以及发布规范。本文将系统性地介绍淘宝开放平台提供的类目相关 API 接口,涵盖类目查询、属性获取、类目树构建等核心能力,并提供完整的 Java 代码实现。
一、接口体系概览
表格
| 接口名称 | 核心功能 | 适用场景 |
|---|---|---|
taobao.itemcats.get | 批量获取商品类目列表 | 构建类目树、全量同步 |
taobao.item.cat.get | 获取单个类目详情 | 查询特定类目信息 |
taobao.itemprops.get | 获取类目属性列表 | 商品发布前的属性规则获取 |
taobao.itempropvalues.get | 获取类目属性值列表 | 获取属性可选值 |
taobao.itemcats.authorize.get | 获取商家授权类目和品牌 | 天猫商家发布商品前校验 |
二、核心接口详解
2.1 taobao.itemcats.get — 批量获取类目列表
请求参数
表格
| 参数名 | 类型 | 必选 | 说明 |
|---|---|---|---|
parent_cid | Number | 条件 | 父类目 ID,0 表示根节点,获取一级类目 |
cids | Number[] | 条件 | 类目 ID 列表,逗号分隔(与 parent_cid 至少传一个) |
fields | String[] | 否 | 返回字段,默认 cid,parent_cid,name,is_parent |
datetime | Date | 否 | 时间戳,用于获取增量变更(暂无法使用) |
响应字段
表格
| 字段名 | 类型 | 说明 |
|---|---|---|
cid | Number | 类目唯一 ID |
parent_cid | Number | 父类目 ID,0 为根类目 |
name | String | 类目名称 |
is_parent | Boolean | 是否为父类目(有子类目则为 true) |
status | String | 状态:normal(正常)、deleted(删除) |
sort_order | Number | 排序值 |
features | Feature[] | 类目特性,如 freeze 表示冻结状态 |
2.2 taobao.item.cat.get — 获取单个类目详情
请求参数
表格
| 参数名 | 类型 | 必选 | 说明 |
|---|---|---|---|
cid | Number | 是 | 类目 ID |
响应示例
JSON
{
"item_cat_get_response": {
"item_cat": {
"cid": 50008163,
"parent_cid": 16,
"name": "女装/女士精品",
"is_parent": true,
"status": "normal",
"sort_order": 1,
"features": [
{
"attr_key": "cat_name",
"attr_value": "女装"
}
]
},
"request_id": "4e9p1x9z8z8z8z8z8z8z8z8z"
}}2.3 taobao.itemprops.get — 获取类目属性
请求参数
表格
| 参数名 | 类型 | 必选 | 说明 |
|---|---|---|---|
cid | Number | 是 | 叶子类目 ID |
type | Number | 否 | 1=淘宝属性,2=天猫属性 |
pid | Number | 否 | 属性 ID,指定则只返回该属性 |
fields | String | 否 | 返回字段 |
属性类型说明
表格
| 属性类型 | 说明 | 示例 |
|---|---|---|
| 关键属性 | 品牌、型号等核心标识 | 品牌=Apple,型号=iPhone 15 |
| 商品属性 | 标准属性字段 | 材质、颜色、尺寸 |
| 绑定属性 | 与 SPU 相关的属性 | 产品库绑定属性 |
| 销售属性 | 影响 SKU 拼接的属性 | 颜色、尺码(不同组合生成不同 SKU) |
三、完整 Java 实现
3.1 核心客户端封装
java
import okhttp3.*;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;import javax.crypto.Mac;import javax.crypto.spec.SecretKeySpec;import java.io.IOException;import java.nio.charset.StandardCharsets;import java.text.SimpleDateFormat;import java.util.*;import java.util.concurrent.TimeUnit;/**
* 淘宝开放平台类目 API Java 客户端
*/public class TaobaoCategoryClient {
private static final String GATEWAY_URL = "https://eco.taobao.com/router/rest";
private static final String APP_KEY = "your_app_key";
private static final String APP_SECRET = "your_app_secret";
private final OkHttpClient httpClient;
public TaobaoCategoryClient() {
this.httpClient = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
}
// ==================== 签名生成 ====================
/**
* 生成 HMAC-MD5 签名
*/
public String generateSign(Map<String, String> params) throws Exception {
// 1. 过滤空值,排除 sign 和 sign_method
List<String> sortedKeys = params.entrySet().stream()
.filter(e -> e.getValue() != null && !e.getValue().isEmpty())
.filter(e -> !e.getKey().equals("sign") && !e.getKey().equals("sign_method"))
.map(Map.Entry::getKey)
.sorted()
.toList();
// 2. 拼接字符串:AppSecret + key1value1 + key2value2 + ... + AppSecret
StringBuilder signStr = new StringBuilder(APP_SECRET);
for (String key : sortedKeys) {
signStr.append(key).append(params.get(key));
}
signStr.append(APP_SECRET);
// 3. HMAC-MD5 加密
Mac mac = Mac.getInstance("HmacMD5");
SecretKeySpec secretKey = new SecretKeySpec(
APP_SECRET.getBytes(StandardCharsets.UTF_8),
"HmacMD5"
);
mac.init(secretKey);
byte[] bytes = mac.doFinal(signStr.toString().getBytes(StandardCharsets.UTF_8));
// 4. 转十六进制大写
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) hexString.append("0");
hexString.append(hex);
}
return hexString.toString().toUpperCase();
}
// ==================== 公共请求方法 ====================
/**
* 执行 API 请求
*/
private JSONObject executeRequest(Map<String, String> params) throws Exception {
// 添加公共参数
params.put("app_key", APP_KEY);
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("format", "json");
params.put("v", "2.0");
params.put("sign_method", "hmac-md5");
// 生成签名
params.put("sign", generateSign(params));
// 构建表单请求
FormBody.Builder formBuilder = new FormBody.Builder();
for (Map.Entry<String, String> entry : params.entrySet()) {
formBuilder.add(entry.getKey(), entry.getValue());
}
Request request = new Request.Builder()
.url(GATEWAY_URL)
.post(formBuilder.build())
.build();
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("HTTP 请求失败: " + response.code());
}
return JSON.parseObject(response.body().string());
}
}
// ==================== 类目查询接口 ====================
/**
* 获取指定父类目下的子类目列表
*
* @param parentCid 父类目 ID,0 表示获取一级类目
* @param fields 返回字段
*/
public List<Category> getItemCats(Long parentCid, String fields) throws Exception {
Map<String, String> params = new HashMap<>();
params.put("method", "taobao.itemcats.get");
params.put("parent_cid", String.valueOf(parentCid));
if (fields != null && !fields.isEmpty()) {
params.put("fields", fields);
}
JSONObject response = executeRequest(params);
// 检查错误
if (response.containsKey("error_response")) {
JSONObject error = response.getJSONObject("error_response");
throw new RuntimeException(
"API错误: [" + error.getString("code") + "] " + error.getString("msg")
);
}
// 解析类目列表
JSONObject itemcatsResponse = response.getJSONObject("itemcats_get_response");
JSONArray itemCats = itemcatsResponse.getJSONArray("item_cats");
List<Category> categories = new ArrayList<>();
if (itemCats != null) {
for (int i = 0; i < itemCats.size(); i++) {
JSONObject cat = itemCats.getJSONObject(i);
categories.add(parseCategory(cat));
}
}
return categories;
}
/**
* 获取单个类目详情
*/
public Category getItemCat(Long cid) throws Exception {
Map<String, String> params = new HashMap<>();
params.put("method", "taobao.item.cat.get");
params.put("cid", String.valueOf(cid));
JSONObject response = executeRequest(params);
if (response.containsKey("error_response")) {
JSONObject error = response.getJSONObject("error_response");
throw new RuntimeException(
"API错误: [" + error.getString("code") + "] " + error.getString("msg")
);
}
JSONObject catResponse = response.getJSONObject("item_cat_get_response");
JSONObject itemCat = catResponse.getJSONObject("item_cat");
return parseCategory(itemCat);
}
/**
* 获取类目属性列表
*/
public List<ItemProp> getItemProps(Long cid, Long type) throws Exception {
Map<String, String> params = new HashMap<>();
params.put("method", "taobao.itemprops.get");
params.put("cid", String.valueOf(cid));
if (type != null) {
params.put("type", String.valueOf(type)); // 1=淘宝, 2=天猫
}
JSONObject response = executeRequest(params);
if (response.containsKey("error_response")) {
JSONObject error = response.getJSONObject("error_response");
throw new RuntimeException(
"API错误: [" + error.getString("code") + "] " + error.getString("msg")
);
}
JSONObject propsResponse = response.getJSONObject("itemprops_get_response");
JSONArray itemProps = propsResponse.getJSONArray("item_props");
List<ItemProp> props = new ArrayList<>();
if (itemProps != null) {
for (int i = 0; i < itemProps.size(); i++) {
JSONObject prop = itemProps.getJSONObject(i);
props.add(parseItemProp(prop));
}
}
return props;
}
// ==================== 数据解析 ====================
private Category parseCategory(JSONObject json) {
Category category = new Category();
category.setCid(json.getLong("cid"));
category.setParentCid(json.getLong("parent_cid"));
category.setName(json.getString("name"));
category.setParent(json.getBooleanValue("is_parent"));
category.setStatus(json.getString("status"));
category.setSortOrder(json.getIntValue("sort_order"));
// 解析 features
JSONArray features = json.getJSONArray("features");
if (features != null) {
Map<String, String> featureMap = new HashMap<>();
for (int i = 0; i < features.size(); i++) {
JSONObject f = features.getJSONObject(i);
featureMap.put(f.getString("attr_key"), f.getString("attr_value"));
}
category.setFeatures(featureMap);
}
return category;
}
private ItemProp parseItemProp(JSONObject json) {
ItemProp prop = new ItemProp();
prop.setPid(json.getLong("pid"));
prop.setName(json.getString("name"));
prop.setIsKeyProp(json.getBooleanValue("is_key_prop"));
prop.setIsSaleProp(json.getBooleanValue("is_sale_prop"));
prop.setIsColorProp(json.getBooleanValue("is_color_prop"));
prop.setIsEnumProp(json.getBooleanValue("is_enum_prop"));
prop.setIsInputProp(json.getBooleanValue("is_input_prop"));
prop.setIsItemProp(json.getBooleanValue("is_item_prop"));
prop.setMust(json.getBooleanValue("must"));
prop.setMulti(json.getBooleanValue("multi"));
prop.setStatus(json.getString("status"));
prop.setSortOrder(json.getIntValue("sort_order"));
// 解析属性值
JSONArray propValues = json.getJSONArray("prop_values");
if (propValues != null) {
List<PropValue> values = new ArrayList<>();
for (int i = 0; i < propValues.size(); i++) {
JSONObject v = propValues.getJSONObject(i);
PropValue pv = new PropValue();
pv.setVid(v.getLong("vid"));
pv.setName(v.getString("name"));
pv.setNameAlias(v.getString("name_alias"));
pv.setStatus(v.getString("status"));
pv.setSortOrder(v.getIntValue("sort_order"));
values.add(pv);
}
prop.setPropValues(values);
}
return prop;
}
// ==================== 数据模型 ====================
public static class Category {
private Long cid;
private Long parentCid;
private String name;
private boolean isParent;
private String status;
private int sortOrder;
private Map<String, String> features;
// Getters & Setters
public Long getCid() { return cid; }
public void setCid(Long cid) { this.cid = cid; }
public Long getParentCid() { return parentCid; }
public void setParentCid(Long parentCid) { this.parentCid = parentCid; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public boolean isParent() { return isParent; }
public void setParent(boolean parent) { isParent = parent; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public int getSortOrder() { return sortOrder; }
public void setSortOrder(int sortOrder) { this.sortOrder = sortOrder; }
public Map<String, String> getFeatures() { return features; }
public void setFeatures(Map<String, String> features) { this.features = features; }
@Override
public String toString() {
return String.format("Category{cid=%d, name='%s', parentCid=%d, isParent=%s}",
cid, name, parentCid, isParent);
}
}
public static class ItemProp {
private Long pid;
private String name;
private boolean isKeyProp; // 是否关键属性
private boolean isSaleProp; // 是否销售属性
private boolean isColorProp; // 是否颜色属性
private boolean isEnumProp; // 是否枚举属性
private boolean isInputProp; // 是否可输入属性
private boolean isItemProp; // 是否商品属性
private boolean must; // 是否必填
private boolean multi; // 是否多选
private String status;
private int sortOrder;
private List<PropValue> propValues;
// Getters & Setters
public Long getPid() { return pid; }
public void setPid(Long pid) { this.pid = pid; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public boolean isKeyProp() { return isKeyProp; }
public void setIsKeyProp(boolean keyProp) { isKeyProp = keyProp; }
public boolean isSaleProp() { return isSaleProp; }
public void setIsSaleProp(boolean saleProp) { isSaleProp = saleProp; }
public boolean isColorProp() { return isColorProp; }
public void setIsColorProp(boolean colorProp) { isColorProp = colorProp; }
public boolean isEnumProp() { return isEnumProp; }
public void setIsEnumProp(boolean enumProp) { isEnumProp = enumProp; }
public boolean isInputProp() { return isInputProp; }
public void setIsInputProp(boolean inputProp) { isInputProp = inputProp; }
public boolean isItemProp() { return isItemProp; }
public void setIsItemProp(boolean itemProp) { isItemProp = itemProp; }
public boolean isMust() { return must; }
public void setMust(boolean must) { this.must = must; }
public boolean isMulti() { return multi; }
public void setMulti(boolean multi) { this.multi = multi; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public int getSortOrder() { return sortOrder; }
public void setSortOrder(int sortOrder) { this.sortOrder = sortOrder; }
public List<PropValue> getPropValues() { return propValues; }
public void setPropValues(List<PropValue> propValues) { this.propValues = propValues; }
}
public static class PropValue {
private Long vid;
private String name;
private String nameAlias;
private String status;
private int sortOrder;
// Getters & Setters
public Long getVid() { return vid; }
public void setVid(Long vid) { this.vid = vid; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getNameAlias() { return nameAlias; }
public void setNameAlias(String nameAlias) { this.nameAlias = nameAlias; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public int getSortOrder() { return sortOrder; }
public void setSortOrder(int sortOrder) { this.sortOrder = sortOrder; }
}}四、递归构建完整类目树
java
import java.util.*;/**
* 类目树构建器
*/public class CategoryTreeBuilder {
private final TaobaoCategoryClient client;
public CategoryTreeBuilder(TaobaoCategoryClient client) {
this.client = client;
}
/**
* 递归构建完整类目树
*/
public CategoryNode buildCategoryTree() throws Exception {
CategoryNode root = new CategoryNode(0L, "根类目", 0);
buildChildren(root);
return root;
}
private void buildChildren(CategoryNode parentNode) throws Exception {
List<TaobaoCategoryClient.Category> children =
client.getItemCats(parentNode.getCid(), "cid,parent_cid,name,is_parent,status,sort_order");
for (TaobaoCategoryClient.Category cat : children) {
CategoryNode childNode = new CategoryNode(
cat.getCid(),
cat.getName(),
parentNode.getLevel() + 1
);
childNode.setParentCid(cat.getParentCid());
childNode.setStatus(cat.getStatus());
childNode.setSortOrder(cat.getSortOrder());
childNode.setLeaf(!cat.isParent());
parentNode.addChild(childNode);
// 递归获取子类目
if (cat.isParent()) {
buildChildren(childNode);
}
}
}
/**
* 查找叶子类目
*/
public List<CategoryNode> findLeafNodes(CategoryNode root) {
List<CategoryNode> leaves = new ArrayList<>();
collectLeaves(root, leaves);
return leaves;
}
private void collectLeaves(CategoryNode node, List<CategoryNode> leaves) {
if (node.isLeaf()) {
leaves.add(node);
return;
}
for (CategoryNode child : node.getChildren()) {
collectLeaves(child, leaves);
}
}
/**
* 根据 CID 查找类目节点
*/
public CategoryNode findByCid(CategoryNode root, Long cid) {
if (root.getCid().equals(cid)) return root;
for (CategoryNode child : root.getChildren()) {
CategoryNode found = findByCid(child, cid);
if (found != null) return found;
}
return null;
}
// ==================== 类目树节点模型 ====================
public static class CategoryNode {
private Long cid;
private String name;
private Long parentCid;
private int level;
private String status;
private int sortOrder;
private boolean isLeaf;
private List<CategoryNode> children = new ArrayList<>();
public CategoryNode(Long cid, String name, int level) {
this.cid = cid;
this.name = name;
this.level = level;
}
public void addChild(CategoryNode child) {
children.add(child);
}
// Getters & Setters
public Long getCid() { return cid; }
public void setCid(Long cid) { this.cid = cid; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Long getParentCid() { return parentCid; }
public void setParentCid(Long parentCid) { this.parentCid = parentCid; }
public int getLevel() { return level; }
public void setLevel(int level) { this.level = level; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public int getSortOrder() { return sortOrder; }
public void setSortOrder(int sortOrder) { this.sortOrder = sortOrder; }
public boolean isLeaf() { return isLeaf; }
public void setLeaf(boolean leaf) { isLeaf = leaf; }
public List<CategoryNode> getChildren() { return children; }
public void setChildren(List<CategoryNode> children) { this.children = children; }
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < level; i++) sb.append(" ");
sb.append(String.format("[%d] %s (cid=%d, leaf=%s)", level, name, cid, isLeaf));
return sb.toString();
}
}
// ==================== 使用示例 ====================
public static void main(String[] args) {
try {
TaobaoCategoryClient client = new TaobaoCategoryClient();
CategoryTreeBuilder builder = new CategoryTreeBuilder(client);
// 构建完整类目树
System.out.println("正在构建类目树...");
CategoryNode root = builder.buildCategoryTree();
// 打印类目树(前三级)
printTree(root, 3);
// 统计叶子类目数量
List<CategoryNode> leaves = builder.findLeafNodes(root);
System.out.println("\n叶子类目总数: " + leaves.size());
// 查找特定类目
CategoryNode dressNode = builder.findByCid(root, 50008163L);
if (dressNode != null) {
System.out.println("\n找到类目: " + dressNode.getName());
System.out.println("层级: " + dressNode.getLevel());
System.out.println("是否叶子: " + dressNode.isLeaf());
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void printTree(CategoryNode node, int maxLevel) {
if (node.getLevel() > maxLevel) return;
System.out.println(node);
for (CategoryNode child : node.getChildren()) {
printTree(child, maxLevel);
}
}}五、商品发布前的类目属性校验
java
/**
* 商品发布前的类目属性校验工具
*/public class CategoryPublishValidator {
private final TaobaoCategoryClient client;
public CategoryPublishValidator(TaobaoCategoryClient client) {
this.client = client;
}
/**
* 校验类目是否可以发布商品
*/
public PublishValidationResult validateCategoryForPublish(Long cid, boolean isTmall) throws Exception {
PublishValidationResult result = new PublishValidationResult();
// 1. 获取类目详情
TaobaoCategoryClient.Category category = client.getItemCat(cid);
result.setCategory(category);
// 2. 检查是否为叶子类目
if (category.isParent()) {
result.addError("类目不是叶子类目,商品必须挂靠在叶子类目下");
return result;
}
// 3. 检查类目状态
if (!"normal".equals(category.getStatus())) {
result.addError("类目状态异常: " + category.getStatus());
return result;
}
// 4. 获取类目属性
Long type = isTmall ? 2L : 1L;
List<TaobaoCategoryClient.ItemProp> props = client.getItemProps(cid, type);
result.setProperties(props);
// 5. 分析必填属性
List<TaobaoCategoryClient.ItemProp> requiredProps = props.stream()
.filter(TaobaoCategoryClient.ItemProp::isMust)
.toList();
result.setRequiredProperties(requiredProps);
// 6. 分析销售属性(影响 SKU)
List<TaobaoCategoryClient.ItemProp> saleProps = props.stream()
.filter(TaobaoCategoryClient.ItemProp::isSaleProp)
.toList();
result.setSaleProperties(saleProps);
result.setValid(result.getErrors().isEmpty());
return result;
}
public static class PublishValidationResult {
private boolean valid;
private TaobaoCategoryClient.Category category;
private List<TaobaoCategoryClient.ItemProp> properties;
private List<TaobaoCategoryClient.ItemProp> requiredProperties;
private List<TaobaoCategoryClient.ItemProp> saleProperties;
private List<String> errors = new ArrayList<>();
public void addError(String error) {
errors.add(error);
valid = false;
}
// Getters & Setters
public boolean isValid() { return valid; }
public void setValid(boolean valid) { this.valid = valid; }
public TaobaoCategoryClient.Category getCategory() { return category; }
public void setCategory(TaobaoCategoryClient.Category category) { this.category = category; }
public List<TaobaoCategoryClient.ItemProp> getProperties() { return properties; }
public void setProperties(List<TaobaoCategoryClient.ItemProp> properties) { this.properties = properties; }
public List<TaobaoCategoryClient.ItemProp> getRequiredProperties() { return requiredProperties; }
public void setRequiredProperties(List<TaobaoCategoryClient.ItemProp> requiredProperties) { this.requiredProperties = requiredProperties; }
public List<TaobaoCategoryClient.ItemProp> getSaleProperties() { return saleProperties; }
public void setSaleProperties(List<TaobaoCategoryClient.ItemProp> saleProperties) { this.saleProperties = saleProperties; }
public List<String> getErrors() { return errors; }
public void setErrors(List<String> errors) { this.errors = errors; }
}}六、Maven 依赖
xml
<dependencies>
<!-- OkHttp HTTP 客户端 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- FastJSON JSON 解析 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.25</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.7</version>
</dependency></dependencies>七、常见错误码
表格
| 错误码 | 错误信息 | 解决方案 |
|---|---|---|
40 | Invalid arguments | 检查必填参数是否完整,cid 是否为数字 |
40001 | Insufficient isv permissions | 应用无权限调用该接口,需在开放平台申请 |
40002 | Invalid timestamp | 时间戳格式错误或与服务端时间偏差超过 10 分钟 |
40003 | Invalid signature | 签名生成错误,检查 HMAC-MD5 算法和参数排序 |
40004 | Invalid method | 接口名称拼写错误 |
40006 | Invalid app_key | App Key 不存在或已失效 |
50001 | API call limit exceeded | 调用频率超限,降低调用频率或申请提升额度 |
isv.invalid-parameter:cid | cid 参数不合法 | 传入的类目 ID 不存在或已删除 |
八、最佳实践建议
- 缓存策略:类目数据变化频率低,建议本地缓存 24-48 小时,减少 API 调用
- 增量更新:定期同步类目变更(关注
status=deleted的类目) - 叶子类目校验:发布商品前务必校验目标类目是否为叶子类目
- 属性缓存:类目属性数据量大,建议按类目分别缓存
- 错误重试:网络异常时实现指数退避重试
- 权限管理:天猫商家需额外调用
itemcats.authorize.get校验授权类目