×

《抖音商品API:/product/detail 与 /product/list 的字段边界与图片合规踩坑》(附 Python 源码)

万邦科技Lex 万邦科技Lex 发表于2026-10-16 14:19:29 浏览44 评论0

抢沙发发表评论

《抖音商品API:/product/detail 与 /product/list 的字段边界与图片合规踩坑》(附 Python 源码)

一、先拍结论

抖店商品域最容易出事的地方不是“调不通”,而是:
把 /product/list 当详情用、把 pic 当 SKU 图用、把白底图当首图用、把平台返回图链当永久 CDN 用、把类目属性当可选填。
  • /product/list:列表视图,适合兜底/增量/比状态,字段是“缩略版”

  • /product/detail:商品真相源,返回规格、SKU、图片、类目、属性、审核态

  • pic:商品轮播主图

  • spec_pics:SKU 规格图,要用 spec_detail_id 关联,不是用 spec_id 硬取

  • 图片合规:首图实物无字、白底图单独传、3:4 或 1:1、无二维码/无极限词/无第三方水印

  • 类目属性:product_format_new 等必填属性不填 → 发品直接被审核打回


二、两个接口的边界(别再混)

维度
/product/list / /product/listV2
/product/detail
定位
商品列表/分页/状态扫描
单品真相
入参
page/page_size/status/update_time
product_id 或 out_product_id
返回
基础字段:标题、状态、价格、主图、类目
完整:spec、sku、spec_pics、desc、类目属性
成本
列表类调用,店群别乱翻页
详情类调用,按商品触发
用法
兜底漏单、状态机推进
写商品主数据、SKU 映射、图片合规校验
坑
以为列表里有完整 SKU 图
以为 detail 不收费/可以无限拉
生产铁律:
list  = 找哪些商品变了
detail = 把变了的那个商品看清楚
sku/list = 拿 SKU 级 spec_detail_id
spec_pics = 按 spec_detail_id 关联 SKU 图
官方明确说过:product/detail 返回 spec_pics,但 不能用 /sku/list 的 spec_id 去取 spec_pics,要用 spec_detail_id1 和 spec_pics.spec_detail_id 关联;图片链接往往还要拼固定域名。

三、核心字段边界

1. 商品级

{
  "product_id": "3558192687276554544",
  "out_product_id": "dy001",        // 新推荐;out_product_id 是老字段,逐渐废弃
  "name": "纯棉短袖T恤 男 夏季",
  "pic": ["https://p3-.../a.jpg"],  // 商品轮播主图,不是 SKU 图
  "status": 1,                       // 上下架
  "check_status": 2,                 // 审核状态
  "cid": 12345,                     // 类目
  "product_format_new": {...},       // 类目必填属性
  "description": "<p>详情页HTML</p>"
}

2. SKU 级

{
  "sku_id": "123",
  "out_sku_id": 456,
  "outer_sku_id": "A001",            // 新推荐 string 版
  "spec_detail_id1": "1001",
  "spec_detail_id2": "1002",
  "spec_detail_name1": "颜色",
  "spec_detail_name2": "尺码",
  "price": 3900,                     // 分
  "stock_num": 10
}

3. 规格图级

{
  "spec_pics": [
    {"spec_detail_id": "1001", "url": "https://p3-.../red.png"},
    {"spec_detail_id": "1002", "url": "https://p3-.../blue.png"}
  ]
}
正确关联:
sku_spec_detail_id = sku["spec_detail_id1"]
sku_img = next(p["url"] for p in spec_pics if p["spec_detail_id"] == sku_spec_detail_id)
错误关联:
sku_img = spec_pics[sku["spec_id"]]   # 错!spec_id ≠ spec_detail_id

四、图片合规:抖店不是“能传上去就合规”

1. 主图规则(官方口径)

  • 第一张主图:商品主体正面实物图

  • 所有主图:除品牌 logo 外,不得有文字/水印

  • 辅图:侧面/背面/平铺/细节,不能全一个角度

  • 主图商品数量/颜色/规格要和 SKU 一致

  • 不得有二维码、微信号、第三方平台水印、非平台链接

2. 比例与尺寸

  • 1:1:800×800,建议 1200×1200

  • 3:4:750×1000,信息流更吃这个

  • 白底图:纯白、无阴影、无 logo、主体占 70%+

  • 详情页:宽 750,总高别超 10000

  • 主图视频:9:16,1080×1920,MP4

3. 白底图误区

白底图不是“首图必须白底”,而是“商城流量/活动报名的加分素材”。
服装鞋包类:首图用实拍/纯色底图,白底图单独传;
全 AI 生成主体、抠图假白底、牛皮癣大字:会被打压/下架/降权。

五、类目与必填属性:发品被打回的重灾区

/product/detail 里你能看到类目和属性,但发品/编辑时:
  • cid 选错 → 类目错放(严重违规,下架+扣分)

  • product_format_new 必填属性没填 → 审核不通过

  • 必填属性要通过 /product/getCatePropertyV2 拿,不是拍脑袋

  • 编辑商品时:spec_prices 是大 JSON,不传的字段不更新,传就要传完整结构

类目属性归一:
pdd_cat_path = "男装 > T恤 > 短袖T恤"
tb_cid3     = 50012029
jd_cid3     = 998
douyin_cid  = 12345

# 跨平台别用 cat_id 直连,用“类目路径 + 属性词”做映射

六、Python:detail / list 拉取 + 字段归一

# douyin/product_client.py
import time
import hashlib
import requests
import hmac


class DouyinProductClient:
    GATEWAY = "https://openapi-fxg.jinritemai.com/"

    def __init__(self, app_key, app_secret, access_token, sign_method="hmac-sha256"):
        self.app_key = app_key
        self.app_secret = app_secret
        self.access_token = access_token
        self.sign_method = sign_method

    def _sign(self, params: dict) -> str:
        qs = "&".join(f"{k}={params[k]}" for k in sorted(params))
        if self.sign_method == "hmac-sha256":
            return hmac.new(
                self.app_secret.encode(), qs.encode(), hashlib.sha256
            ).hexdigest()
        return hashlib.md5(
            (self.app_secret + qs + self.app_secret).encode()
        ).hexdigest().upper()

    def _common(self, method: str):
        return {
            "method": method,
            "app_key": self.app_key,
            "access_token": self.access_token,
            "timestamp": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
            "v": "2",
            "sign_method": self.sign_method,
            "format": "json",
        }

    def list_products(self, status=None, page=1, page_size=100, start=None, end=None):
        biz = {"page": page, "page_size": page_size, "order_by": 1}
        if status is not None:
            biz["status"] = status
        if start:
            biz["start_time"] = start
        if end:
            biz["end_time"] = end
        params = self._common("product.list")
        params["param_json"] = str(biz).replace("'", '"')
        params["sign"] = self._sign(params)
        return requests.post(self.GATEWAY, data=params, timeout=5).json()

    def get_detail(self, product_id=None, out_product_id=None, show_draft=False):
        biz = {"show_draft": show_draft}
        if product_id:
            biz["product_id"] = product_id
        elif out_product_id:
            biz["out_product_id"] = out_product_id
        else:
            raise ValueError("product_id/out_product_id 至少传一个")
        params = self._common("product.detail")
        params["param_json"] = str(biz).replace("'", '"')
        params["sign"] = self._sign(params)
        return requests.post(self.GATEWAY, data=params, timeout=5).json()
抖店 param_json 是字符串化 JSON,不是散参数;timestamp 是 yyyy-MM-dd HH:mm:ss(GMT+8)。

七、Python:SKU 图按 spec_detail_id 关联

# douyin/spec_pic_resolver.py
from dataclasses import dataclass
from typing import Dict, List, Optional


@dataclass
class ResolvedSku:
    sku_id: str
    outer_sku_id: Optional[str]
    attrs: Dict[str, str]
    price_yuan: float
    stock: int
    sku_img: Optional[str]

# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
def resolve_skus(detail: dict) -> List[ResolvedSku]:
    d = (detail.get("data") or {})
    spec_pics = {p["spec_detail_id"]: p["url"] for p in d.get("spec_pics", [])}

    out = []
    for sku in d.get("skus", d.get("spec_prices", [])):
        attrs = {}
        for i in (1, 2, 3):
            name = sku.get(f"spec_detail_name{i}")
            val = sku.get(f"spec_detail_value{i}") or sku.get(f"spec{i}_name")
            if name:
                attrs[name] = val

        # 一级规格图最常见;多级可拼 [sd1, sd2]
        sd = sku.get("spec_detail_id1")
        sku_img = spec_pics.get(sd) if sd else None

        price_fen = sku.get("price") or sku.get("sku_price") or 0
        stock = sku.get("stock_num") or sku.get("available_stock") or 0

        out.append(ResolvedSku(
            sku_id=str(sku["sku_id"]),
            outer_sku_id=sku.get("outer_sku_id") or str(sku.get("out_sku_id", "")),
            attrs=attrs,
            price_yuan=price_fen / 100.0,
            stock=int(stock),
            sku_img=sku_img,
        ))
    return out

八、Python:图片合规预检(上架前拦废图)

# douyin/image_compliance.py
import re
from urllib.parse import urlparse


FORBIDDEN_TEXT_HINTS = ["微信", "加我", "扫码", "薇信", "私聊", "淘宝", "京东", "拼多多"]
EXT_OK = {".jpg", ".jpeg", ".png"}
LIMIT_MB = 3


class ImageComplianceError(Exception):
    pass


def check_main_images(pics: list, first_must_be_real=True):
    if not pics or len(pics) < 3:
        raise ImageComplianceError("主图至少 3 张")
    if len(pics) > 9:
        raise ImageComplianceError("主图最多 9 张")

    for url in pics:
        p = urlparse(url)
        ext = "." + p.path.rsplit(".", 1)[-1].lower()
        if ext not in EXT_OK:
            raise ImageComplianceError(f"格式不支持: {url}")
        if any(h in url.lower() for h in ("qrcode", "wechat", "vip")):
            raise ImageComplianceError(f"疑似二维码/站外水印: {url}")

# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
def check_title(title: str):
    if not title:
        raise ImageComplianceError("标题为空")
    if len(title) > 30:
        raise ImageComplianceError("标题超过 30 个汉字")
    bad = ["第一", "最高级", "全网最低", "百分百", "根治", "包治", "最便宜"]
    hit = [w for w in bad if w in title]
    if hit:
        raise ImageComplianceError(f"标题含极限/违规词: {hit}")


def check_attr_completeness(product_format_new: dict, required_keys: list):
    missing = [k for k in required_keys if not product_format_new.get(k)]
    if missing:
        raise ImageComplianceError(f"类目必填属性缺失: {missing}")
调用顺序:
check_title(name)
check_main_images(pic)
check_attr_completeness(product_format_new, required_keys)
# 过了再进 addV2 / editV2

九、list → detail → sku 的生产流水线

/product/listV2
   └─ 拿到 product_id + status + update_time
        └─ 状态/时间变化 → 进待同步队列

Worker:
   /product/detail(product_id)
      ├─ 归一商品主数据(标题/类目/主图/详情)
      ├─ resolve_skus()  → SKU 表
      ├─ spec_pics 按 spec_detail_id 关联
      ├─ check_title / check_main_images / check_attr_completeness
      └─ 写 erp_spu / erp_sku / platform_product_snapshot

编辑回写:
   /product/editV2
      - spec_prices 要传完整结构
      - 不传的字段不会被更新

十、编辑商品的两个暗雷

1. spec_prices 是整体结构

编辑时你只改价格:
{"spec_prices": [{"sku_id": 1, "price": 3990}]}
❌ 会丢库存/规格名/编码。
✅ 必须从 detail 里读完整 spec_prices,改其中字段再回写。

2. 审核中和在售商品

  • 编辑在售商品:审核通过后才会覆盖线上数据

  • 审核中商品:别并发编辑,别用 list 里的缩略数据回写 detail 级字段


十一、跨平台 SKU 归一(接前几篇)

{
  "platform": "douyin",
  "spu_id": product_id,
  "sku_id": sku_id,
  "outer_sku_id": outer_sku_id,
  "attrs": {"颜色": "红", "尺码": "M"},
  "sku_fingerprint": sha1(json.dumps(sorted(attrs))),
  "price": 39.9,
  "stock": 10,
  "images": {
     "product_pics": [...],     # 商品主图
     "sku_spec_pic": "https://..."  # 规格图,按 spec_detail_id 关联
  }
}
规则:
  • pic ≠ spec_pics

  • spec_id ≠ spec_detail_id

  • out_sku_id(int) ≠ outer_sku_id(string)

  • 跨平台别用平台 cat_id 直连,用类目路径+属性词


十二、落地检查表

  • [ ] list 只做“哪些商品变了”,不写主数据

  • [ ] detail 才写 SPU/SKU/图片/属性

  • [ ] SKU 图用 spec_detail_id 关联 spec_pics

  • [ ] 主图 ≥3 张、≤9 张,首图实物无字

  • [ ] 白底图单独传,不强制当首图

  • [ ] 3:4 / 1:1 都支持,服装鞋包优先 3:4

  • [ ] 标题 ≤30 汉字,无极限词/站外词

  • [ ] 详情页无二维码/微信号/第三方水印

  • [ ] product_format_new 必填属性用 getCatePropertyV2 拿

  • [ ] editV2 的 spec_prices 传完整结构

  • [ ] 图片 URL 拼域名后再存,别直接信相对路径

  • [ ] 列表调用和详情调用分桶计费(2026.7.1 起都值钱)


十三、一句话收口

淘宝商品难在 properties_name,京东难在 skuList 嵌套,拼多多难在 specs + sku_list.specs 自己笛卡尔,
抖店商品难在:list 和 detail 的字段边界、spec_id 和 spec_detail_id 的图关联、首图和白底图的合规语义、类目必填属性的审核拦截。
谁把 /product/list 的 pic 当成 SKU 图、把白底图塞成首图、把 spec_id 拿去查 spec_pics、把必填属性当可选填——
谁就会看到:商品能发上去,但流量起不来、审核被打回、SKU 图串色、跨平台主数据全歪。


群贤毕至

访客