《淘宝商品API:taobao.item.get 与 taobao.item.seller.get 的权限边界与字段缺失踩坑》(附 Python 源码)
先拍结论:
taobao.item.get= 商品公开/半公开视图(无 session 拿公开字段,有 session 拿更多);taobao.item.seller.get= 卖家自有商品后台视图(必须卖家 session,拿 SKU 库存/属性/详情/店铺内类目等经营数据)。两者不是“同一个接口的两个名字”,而是“买家/竞品视角” vs “店主/ERP 视角”。字段缺失 90% 不是接口坏了,而是:没授权、fields 没要、类目无此字段、私密字段不返回、sold_quantity本来就不给。
一、权限边界一张表
维度 | taobao.item.get | taobao.item.seller.get |
|---|---|---|
视角 | 商品详情(买家/竞品/选品) | 卖家自有商品(店铺后台) |
是否要卖家 session | 否(公开字段);传 session 可扩字段 | 必须 session(卖家授权) |
能查谁的商品 | 任意淘宝/天猫商品(权限允许时) | 只能查授权店铺自己的商品 |
典型用途 | 竞品监控、铺货源、价格/主图/SKU 公开信息 | ERP 商品同步、库存、SKU 编码、详情 HTML、店铺类目 |
私密字段 | 拿不到(利润/采购价/全量私域属性) | 能拿更多,但平台控字段 |
下架/仓库商品 | 通常查不到或返回空 | 能查(配合 items.inventory.get 找 ID) |
常见误用 | 拿它当“店铺后台全量接口” | 拿它查竞品/别的店 → 空/权限错 |
官方口径很直白:
taobao.item.get “卖家未登录时只能获得这个商品的公开数据,卖家登录后可以获取商品的所有数据”;taobao.item.seller.get 走 session=xxx 查本店商品。二、字段缺失的 6 个真因(别先怀疑 SDK)
1️⃣ fields 没传 / 传了不存在字段
淘宝是“你问什么给什么”:
fields=num_iid,title,price → 只返回这 3 个;skus / desc / item_imgs 根本不在响应里
传不存在字段会报
Invalid Parameter。✅ 按场景维护 fields 白名单。
2️⃣ 没 session → 公开层自动砍字段
taobao.item.get 无 session:给:title / pic_url / price / cid / nick / detail_url
不给/不稳定:详细 SKU 库存、私域属性、店铺内类目、采购底价、部分销量
有 session:
返回集合扩大,但是否返回仍看应用权限包
3️⃣ sold_quantity 官方就不返回
taobao.item.get 文档明确:Item 结构体所有字段可返回,除 sold_quantity。你还去
item["sold_quantity"] 取值 = 永远 None。✅ 销量要么用
taobao.items.search 的搜索层字段,要么接受“平台不给你真实已售”。4️⃣ 类目/商品类型不支持该字段
虚拟商品(话费/点卡):库存/SKU 语义不同
秒杀商品:编辑类字段受限
3D 商品 / 淘1站 / 秒杀标记:部分字段有特殊含义或不可改
✅ 解析前先读
type / approve_status / second_kill / is_3D。5️⃣ 下架/删除/违规商品
taobao.item.get查别人下架商品 → 空 / invalidtaobao.item.seller.get才能查自己仓库里商品,但也要先从items.inventory.get拿到 num_iid
✅ 全店商品 =
items.onsale.get + items.inventory.get + 按 id 补 item.seller.get。6️⃣ 应用没签“商品读”/“增值”权限
淘宝是最小授权:
基础商品读:title/price/pic 可能给
SKU 库存、详情 HTML、视频、属性全景:要对应权限包
CRM/会员/转化/流量:增值,塔内+签约
看到:
{"error_response":{"code":21,"msg":"Check App Permission Fail"}}不是限流,是AppKey 没这个字段的权限。
三、生产向:两个接口的“职责分离”
竞品/选品/铺货源 └─ taobao.item.get(num_iid + 公开 fields) ├─ title / pic_url / price / cid / props_name ├─ skus(公开部分) └─ 不依赖卖家 session 自有店铺 ERP 同步 └─ items.onsale.get / items.inventory.get (拿 num_iid 列表) └─ taobao.item.seller.get(session=卖家token) ├─ skus.num / outer_id / barcode ├─ desc / item_imgs / video ├─ seller_cids / input_pids / input_str └─ 库存/规格/店铺内类目
规则:用 item.get 做“全网商品理解”,用 item.seller.get 做“本店商品资产”。把 item.seller.get 拿到的 SKU 编码/库存,当成 ERP 主数据;把 item.get 拿到的价格/主图,当成“前台展示/铺货源”。
四、Python:统一商品适配器
# top/item_adapter.py
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class TopItemView:
num_iid: str
title: Optional[str] = None
price: Optional[float] = None
pic_url: Optional[str] = None
cid: Optional[str] = None
nick: Optional[str] = None
skus: list[dict] = field(default_factory=list)
desc: Optional[str] = None
outer_id: Optional[str] = None
stock_total: Optional[int] = None
is_owner_view: bool = False
missing_fields: list[str] = field(default_factory=list)
raw: dict = field(default_factory=dict)
class TopItemClient:
PUBLIC_FIELDS = [
"num_iid", "title", "price", "pic_url", "cid", "nick",
"detail_url", "item_imgs", "props_name", "skus",
]
OWNER_FIELDS = [
"num_iid", "title", "price", "pic_url", "cid",
"skus", "desc", "item_imgs", "video",
"seller_cids", "input_pids", "input_str", "outer_id",
"num", "modified",
]
def __init__(self, top_client):
| # top_client.execute(method, params)
self.top = top_client
def get_public_item(self, num_iid: str) -> TopItemView:
resp = self.top.execute("taobao.item.get", {
"num_iid": num_iid,
"fields": ",".join(self.PUBLIC_FIELDS),
})
item = (resp.get("item_get_response") or {}).get("item") or {}
return self._normalize(item, owner=False)
def get_owner_item(self, num_iid: str, seller_session: str) -> TopItemView:
resp = self.top.execute("taobao.item.seller.get", {
"num_iid": num_iid,
"fields": ",".join(self.OWNER_FIELDS),
"session": seller_session,
})
item = (resp.get("item_seller_get_response") or {}).get("item") or {}
return self._normalize(item, owner=True)
def _normalize(self, item: dict, owner: bool) -> TopItemView:
want = set(self.OWNER_FIELDS if owner else self.PUBLIC_FIELDS)
got = set(k for k, v in item.items() if v not in (None, "", [], {}))
view = TopItemView(
num_iid=str(item.get("num_iid")),
title=item.get("title"),
price=_f(item.get("price")),
pic_url=item.get("pic_url"),
cid=item.get("cid"),
nick=item.get("nick"),
skus=item.get("skus", {}).get("sku", []) if isinstance(item.get("skus"), dict) else item.get("skus", []),
desc=item.get("desc"),
outer_id=item.get("outer_id"),
stock_total=_i(item.get("num")),
is_owner_view=owner,
missing_fields=sorted(want - got),
raw=item,
)
return view消费侧铁律:
view = client.get_public_item(num_iid) if "skus" in view.missing_fields and not view.is_owner_view: # 不是接口挂了:公开视图本来可能不给完整库存 fallback_to_owner_or_skip(view)
五、SKU 解析:淘宝 SKU 是“属性组合”,不是“尺寸+颜色”两列
def parse_taobao_skus(skus: list[dict]):
out = []
for s in skus:
out.append({
"sku_id": s.get("sku_id"),
"outer_id": s.get("outer_id"),
"barcode": s.get("barcode"),
"price": _f(s.get("price")),
"quantity": _i(s.get("quantity")),
# properties_name: "1627207:28332:颜色:黑;20509:28381:尺码:XL"
"attrs": _parse_props(s.get("properties_name", "")),
})
return out
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
def _parse_props(text: str):
attrs = {}
for seg in text.split(";"):
seg = seg.strip()
if not seg:
continue
# pid:vid:name:value
parts = seg.split(":")
if len(parts) >= 4:
_, _, name, value = parts[0], parts[1], parts[2], parts[3]
attrs[name] = value
return attrs跨平台映射时:别用“颜色/尺寸”硬字段,用attrs字典 + SKU 指纹,不然铺到 Mercari/闲鱼/1688 全串。
六、字段缺失兜底策略(ERP 必须写)
def safe_publish_payload(view: TopItemView) -> dict:
payload = {
"source": "taobao",
"num_iid": view.num_iid,
"title": view.title or "未授权标题",
"price": view.price,
"images": [view.pic_url] if view.pic_url else [],
}
if not view.skus:
payload["warn"] = "no_sku:item.get 无 session 或字段未授权"
if view.desc is None:
payload["warn"] = (payload.get("warn") or "") + ";desc_not_granted"
if view.outer_id is None and view.is_owner_view:
payload["warn"] = (payload.get("warn") or "") + ";outer_id_missing"
# 公开视图禁止声称“我有完整库存”
if not view.is_owner_view:
payload["inventory_trusted"] = False
else:
payload["inventory_trusted"] = view.stock_total is not None
return payload七、限流与调用策略
taobao.item.get:公开采集接口,QPS 低、日额度低、大促限流狠taobao.item.seller.get:按卖家 session 再叠一层用户级限流列表先拿 ID,详情后补;别用 item 接口硬翻全店
字段按需要,别
fields=*(淘宝也没有真*,但乱传字段会错)
BUCKET_ITEM_GET = TokenBucket(rate=5) # 官方可能 2~10,留余量 BUCKET_SELLER_GET = TokenBucket(rate=8) # 本店私有接口别猛打 def sync_owner_catalog(seller_session, client): ids = [] ids += list_onsale_ids() # taobao.items.onsale.get ids += list_inventory_ids() # taobao.items.inventory.get for num_iid in ids: BUCKET_SELLER_GET.acquire() view = client.get_owner_item(num_iid, seller_session) upsert_catalog(view)
八、和前几篇收口
1688 商品 API:
offer/item/product/sku是供货视角,retailPrice/consignPrice是分销价淘宝 TOP 认证限流:item.get 走应用凭证,item.seller.get 必须卖家 session
1688 图片空间:淘宝
pic_url同理——img.alicdn.com别直链,转存消息服务:商品变更消息只触发,真相用
item.seller.get拉本文:
item.get回答“这个商品对外长啥样”,item.seller.get回答“我店里这个商品到底有哪些 SKU/库存/属性”。用错接口 = 竞品监控拿不到库存,店铺 ERP 拿不到竞品。
九、一句话收口
淘宝商品 API 的坑不在签名,在视角:无 session 的item.get永远只是“橱窗”;有 session 的item.seller.get才是“仓库和货架”。字段没了先问三句——“你传 fields 了吗?你带 session 了吗?这个字段平台本来给吗?”三句答完,80% 的“接口坏了”都会变成“权限模型没设计对”。