结论先拍:苏宁开放平台在家电3C/自营联营场景里接口最全(商品/价格/库存/订单/发货/厂送/政企采购全链路),但收费模型和淘宝/拼多多不同——2021.5起已取消“星河云内/云外10倍差”,统一基础0.1元/百次、增值0.3元/百次;订单发货按0.01元/单另收佣金;预充值模式、欠费7天限流14天停服。 做家电3C自研ERP,重点是库存用suning.govbus.inventory.get精准查、订单走suning.custom.order.query增量、发货回写走suning.custom.orderdelivery.add(每单0.01元),别把苏宁当淘宝抄架构。
一、家电3C场景核心接口地图
业务域 | 关键method | 收费档 | 3C场景用处 |
|---|---|---|---|
商品 | suning.govbus.item.get / product/batchGet | 基础0.1/百次 | 家电型号/参数/主图抓取 |
价格 | suning.price.query / batchQuery | 基础 | 比价、调价监控 |
库存 | suning.govbus.inventory.get(单SKU精准,默认50/次最大100) | 免费 | 3C防超卖必用,不占计费额度 |
订单增量 | suning.custom.order.query / suning.selfmarket.saleorder.query | 基础 | 自营/联营/厂送订单拉取 |
单订单 | suning.custom.order.get | 基础 | 明细/串码/安装标识 |
发货 | suning.custom.orderdelivery.add | 基础调用费 + 0.01元/单佣金 | 出库回写,3C大件必走 |
逆向 | suning.aftersale.refund.query / 换货API | 基础 | 退换修(家电高频) |
物流 | suning.custom.logisticcompany.get + 面单 | 基础 | 大件预约配送 |
政企 | suning.govbus.order.add / ordernumber.query | 基础 | 招投标/企业采购对接 |
图片上传 | suning.custom.npic.add | 免费(2021.5起从基础降为免费) | 商品主图维护 |
家电3C特殊性:串码(IMEI/SN)跟踪、厂送安装预约、国补凭证上传、虚拟号更新这些在suning.selfmarket.*族里,自营供应商必接。
二、收费口径(2026现行,三句话讲清)
- 基础API统一 0.1元/百次(不分云内云外,原“河内=河外/10”2021.5已废除)
- 增值API 0.3元/百次(批量解密/风控/会员通等,文档标“增值”的才走这档)
- 订单发货佣金 0.01元/单(仅
orderdelivery.add类发货接口触发,按运单计) - 预充值+按日结算:今天跑的调用明天扣,余额0后7天限流、14天停服,不是429是断气
- 免费接口:
govbus.inventory.get、custom.npic.add、部分政企查询——3C库存同步零费是官方给的洼地
算账:单店日调订单增量4000+商品2000+价格1000=7000次/天 → 月21万次×0.1/百=¥210/月;若日发货100单×0.01=¥3/月发货佣金;比拼多多云内贵(拼0.01/百次),比抖店云内(0.018/百次)略贵一档,但没有云外10倍惩罚。
三、家电3C自研ERP架构要点
- 库存零费套路:
govbus.inventory.get免费,但单Key QPS实测约5/s(官方未强宣,社区实测5QPS/日5万上限),做3C防超卖用“详情页单查+加购校验+提交订单批查”三层,别无脑轮询所有SKU。 - 订单增量重叠窗:
order.query用startTime/endTime每5分钟拉一次,3C单量少但客单价高,漏单损失远大于API费。 - 发货单佣金要单列科目:0.01元/单看起来忽略不计,但3C大促日发5000单=¥50/天,月¥1500,做ISV转嫁时要写进服务定价。
- 厂送/自营双通道:自营走
selfmarket.saleorder.query,POP店走custom.order.query,别混AppKey。 - 预充值守卫移植:和拼多多同思路——本地反推预估余额,低于3天预估熔断非核心(商品爬价可断,发货不能断)。
四、Python:SuGuardedClient(家电3C场景版)
# suning_guarded_client.py
"""
苏宁开放平台 GuardedClient(家电3C/自营联营场景)
- 基础0.1/百次, 增值0.3/百次, 发货0.01/单
- 免费接口(inventory.get/npic.add)不走计费计数器
- 预充值余额守卫(本地反推+按日校准)
- 捕获欠费/限流特征
"""
import time, hashlib, requests, json
from typing import Dict, Optional
GW = "https://open.suning.com/api/http/sopRequest"
BASE_UNIT = 0.1 / 100
ADV_UNIT = 0.3 / 100
PER_ORDER_FEE = 0.01
FREE_METHODS = {
"suning.govbus.inventory.get",
"suning.custom.npic.add",
"suning.govbus.ordernumber.query",
}
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
class SuGuardedClient:
def __init__(self, app_key, app_secret,
cached_balance=None,
est_daily_calls=7000,
warn_days=3,
recharge_in_flight=False):
self.ak = app_key
self.ask = app_secret
self.official_balance = cached_balance
self.local_spent = 0.0
self.est_daily = est_daily_calls
self.warn_days = warn_days
self.recharge_in_flight = recharge_in_flight
@property
def est_balance(self):
if self.official_balance is None:
return None
return self.official_balance - self.local_spent
def sync_official_balance(self, fresh: float):
self.official_balance = fresh
self.local_spent = 0.0
def _sign(self, params: Dict) -> str:
# 苏宁:按key升序,空值跳过,拼接 param=val,md5(appSecret+拼接+appSecret) 小写
f = sorted((k, v) for k, v in params.items()
if v is not None and str(v).strip() != "" and k != "sign")
qs = "".join(f"{k}{v}" for k, v in f)
return hashlib.md5(f"{self.ask}{qs}{self.ask}".encode("utf-8")).hexdigest().lower()
def _tier(self, method: str):
if method in FREE_METHODS:
return "free"
if method.startswith("suning.custom.orderdelivery") or "delivery" in method:
return "per_order"
if "decrypt" in method or "member" in method or "risk" in method:
return "adv"
return "base"
def _before(self, method: str, is_core: bool):
tier = self._tier(method)
if tier == "free":
return tier
bal = self.est_balance
if bal is None:
return tier
unit = ADV_UNIT if tier == "adv" else BASE_UNIT
day_cost = self.est_daily * unit
if bal <= 0:
if self.recharge_in_flight and not is_core:
raise RuntimeError("⏸ 苏宁预估余额≤0,充值在途,非核心熔断")
raise RuntimeError("🚨 苏宁预充值余额≤0(7天限流/14天停服),立即充值")
if bal < day_cost * self.warn_days and not is_core:
raise RuntimeError(f"⏸ 余额¥{bal:.2f}<{self.warn_days}天预估,非核心熔断")
return tier
@staticmethod
def _is_fee_err(d: Dict) -> bool:
if "error_response" not in d:
return False
er = d["error_response"]
code = str(er.get("code", ""))
blob = json.dumps(er, ensure_ascii=False).lower()
return code in ("50001", "isp.sys.service.unavailable.gcapi") or "balance" in blob \
or "fee" in blob or "insufficient" in blob or "quota" in blob
def safe_call(self, method, biz, *, is_core=True, max_retry=3):
tier = self._before(method, is_core)
params = {
"app_key": self.ak,
"method": method,
"timestamp": str(int(time.time()*1000)),
"format": "json",
"v": "1.0",
"sign_method": "md5",
}
params.update(biz)
params["sign"] = self._sign(params)
for att in range(max_retry):
try:
r = requests.post(GW, data=params, timeout=15)
d = r.json()
if self._is_fee_err(d):
unit = ADV_UNIT if tier == "adv" else BASE_UNIT
self.local_spent += self.est_daily * unit
if self.recharge_in_flight and att < max_retry-1:
time.sleep(20); continue
raise RuntimeError(f"🚨 苏宁欠费/限流: {d['error_response']}")
if "error_response" in d:
raise Exception(f"SUNING_ERR[{d['error_response'].get('code')}]: {d['error_response'].get('msg')}")
# 成功扣费
if tier == "free":
pass
elif tier == "per_order":
self.local_spent += PER_ORDER_FEE
else:
self.local_spent += BASE_UNIT if tier == "base" else ADV_UNIT
return d
except requests.RequestException:
if att < max_retry-1:
time.sleep(2**att); continue
raise
# 3C场景业务方法
def query_inventory_free(self, city_id, county_id, sku_ids):
# 免费接口,不计数
return self.safe_call("suning.govbus.inventory.get",
{"cityId": city_id, "countyId": county_id, "skuIds": sku_ids})
def query_orders_inc(self, token, start, end, page=1, page_size=50):
return self.safe_call("suning.custom.order.query",
{"startTime": start, "endTime": end,
"pageNo": page, "pageSize": page_size}, is_core=True)
def deliver_order(self, token, order_id, express_code, express_no):
# 触发0.01元/单
return self.safe_call("suning.custom.orderdelivery.add",
{"orderId": order_id, "expressCompanyCode": express_code,
"expressNo": express_no}, is_core=True)
if __name__ == "__main__":
cli = SuGuardedClient("AK", "AS", cached_balance=50.0, est_daily_calls=7000)
# 免费库存查询
try:
cli.query_inventory_free("010", "10", "123456,789012")
except RuntimeError as e:
print(e)
# 订单增量
try:
cli.query_orders_inc("TOKEN", "2026-08-01 00:00:00", "2026-08-01 00:05:00")
except RuntimeError as e:
print(e)
# 发货(0.01/单)
try:
cli.deliver_order("TOKEN", "11111", "SNWL", "SF1234567890")
except RuntimeError as e:
print(e)五、和前几家对照(CTO选型用)
平台 | 基础单价 | 云外惩罚 | 预充值 | 增值禁外 | 3C适配 |
|---|---|---|---|---|---|
淘宝TOP | 0.02/百次(内) | ×10 | 否 | 是 | 中(偏服饰百货) |
拼多多 | 0.01/百次(内) | ×10 | 是 | 是 | 弱(3C少) |
抖店 | 0.018/百次(内) | ×10 | 是 | 是 | 弱 |
苏宁 | 0.1/百次统一 | 无(已废) | 是 | 增值档存在 | 强(自营+厂送+串码) |
京东JOS | 0.05/百次(内) | 2~10倍 | 否 | 部分 | 强(但联盟/商家Key分) |
苏宁的“贵”在基础单价0.1比淘宝拼多多高一个数量级,但没有云外陷阱、库存查询免费、发货按单0.01透明,对家电3C自研(单量不大、客单高、要串码和厂送)反而可预期。
六、三条必踩坑提醒
- 别把
govbus.inventory.get当收费接口防:它免费但单Key QPS约5,硬轮询5000个3CSKU会限流,必须“详情页单查+加购校验”降级调用。 - 发货佣金0.01/单要进定价:ISV卖SAAS给家电经销商,这笔钱要么商家自充要么转嫁,别和API调用费混在一起算亏。
- 欠费14天停服是硬规则:苏宁不像淘宝超量后付费,预充值断了7天限流14天全停,本地余额守卫比拼多多还急(因为没云外退路)。
一句话定性:苏宁API在家电3C里是“接口最贴场景、收费最透明、但没有便宜到可以忽略”的一家;0.1元/百次统一价+库存免费+发货0.01/单,架构做对(免费库存兜底+订单增量+发货单列科目)单店月费可以压到几百块,比迁云折腾拼多多/抖店云内简单得多——它不逼你入云,只逼你别欠费。
要不要我把
SuGuardedClient 改成 Redis中心化余额(多进程)+ 苏宁订单推送(若有消息订阅)Consumer + 发货0.01/单独立台账,直接拼进你前面对接九家的中台调度器?