🚚 1688物流跟踪API:实时查询快递轨迹对接方案(附Python源码)
1688的物流跟踪主要通过两个接口完成:① 查询订单发货物流信息(alibaba.logistics.trade.ship / alibaba.logistics.order.get)和 ② 订阅/解析运单轨迹(alibaba.logistics.trace.get)。对于ERP/WMS系统,核心目标是:拿到1688发货的运单号 → 定时拉取物流轨迹 → 回写ERP出库单状态。
一、1688物流对接的两个核心接口
接口 用途 关键返回
alibaba.logistics.trade.ship
(或 alibaba.logistics.order.get) 查某采购单的发货记录 logisticsCode(快递公司码)、billNo(运单号)、sendTime
alibaba.logistics.trace.get 根据companyCode+billNo查实时轨迹 签收状态、节点时间、当前城市
⚠️ 前提:应用需申请物流查询权限(自用型应用默认可申请),订单须是已发货状态才有数据。
二、Python封装:查运单号 + 拉取轨迹
ali1688_logistics.py
import hashlib
import time
import requests
import urllib.parse
from typing import Dict, List, Optional
from datetime import datetime, timedelta
封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
class Ali1688LogisticsClient:
"""
1688 物流跟踪 Client
网关与签名规则同标准1688 Open API
"""
GATEWAY = "https://gw.open.1688.com/openapi/http/2/1"
def __init__(self, app_key: str, app_secret: str, access_token: str):
self.app_key = app_key
self.app_secret = app_secret
self.access_token = access_token
# ─────────────── 签名(MD5) ───────────────
def _sign(self, params: Dict) -> str:
filtered = sorted((k, v) for k, v in params.items() if v is not None)
qs = ''.join(f"{k}{v}" for k, v in filtered)
raw = f"{self.app_secret}{qs}{self.app_secret}"
return hashlib.md5(raw.encode('utf-8')).hexdigest().upper()
def _call(self, method: str, biz: Dict) -> Dict:
api_params = {
"method": method,
"app_key": self.app_key,
"session": self.access_token,
"timestamp": str(int(time.time() * 1000)),
"format": "json",
"v": "2.0",
"sign_method": "md5",
}
api_params["param2"] = urllib.parse.quote_plus(
str(biz).replace("'", '"')
)
api_params["sign"] = self._sign(api_params)
resp = requests.get(self.GATEWAY, params=api_params, timeout=15)
resp.raise_for_status()
data = resp.json()
if "error_response" in data:
err = data["error_response"]
raise Exception(f"1688 Logistics Err[{err.get('code')}]: {err.get('msg')}")
result_key = [k for k in data if k != "error_response"][0]
return data[result_key]
# ─────────────── ① 查订单发货物流 ───────────────
def get_order_logistics(self, order_id: str) -> List[Dict]:
"""
返回该订单下所有物流单
每个元素含: logisticsCode(快递码), billNo(运单号), companyName
"""
biz = {"orderId": order_id}
res = self._call("alibaba.logistics.trade.ship", biz)
orders = res.get("logisticsOrders", []) or []
result = []
for lo in orders:
result.append({
"logistics_code": lo.get("logisticsCode"), # 如 "YTO" "SF"
"logistics_name": lo.get("logisticsCompanyName"),
"bill_no": lo.get("billNo") or lo.get("mailNo"),
"send_time": lo.get("gmtSend"),
"consignee": lo.get("consigneeName")
})
return result
# ─────────────── ② 查运单轨迹 ───────────────
def get_trace(self, company_code: str, bill_no: str) -> Dict:
"""
company_code: 1688返回的 logisticsCode (YTO/ZJS/SF...)
bill_no: 运单号
返回含 signStatus(已签/未签) + traceList
"""
biz = {
"companyCode": company_code,
"billNo": bill_no
}
res = self._call("alibaba.logistics.trace.get", biz)
return {
"sign_status": res.get("signStatus"), # SIGN 已签 / UNSIGN 未签
"sign_time": res.get("signTime"),
"traces": res.get("traceList") or []
}
=========================================================
使用示例:同步1688采购单物流 → 回写ERP
=========================================================
if name == "main":
client = Ali1688LogisticsClient(
app_key="YOUR_APP_KEY",
app_secret="YOUR_APP_SECRET",
access_token="YOUR_ACCESS_TOKEN"
)
ORDER_ID = "2338123456789000" # 1688采购单号
try:
# ① 获取运单
logistics = client.get_order_logistics(ORDER_ID)
if not logistics:
print("⚠️ 该订单尚未发货或无物流信息")
exit()
for lg in logistics:
print(f"\n📦 {lg['logistics_name']} 运单:{lg['bill_no']}")
# ② 查轨迹
trace = client.get_trace(lg["logistics_code"], lg["bill_no"])
print(f" 签收状态: {trace['sign_status']} {trace['sign_time'] or ''}")
for node in (trace["traces"] or []):
print(f" [{node.get('time')}] {node.get('desc')} {node.get('city','')}")
# ③ ERP联动(伪代码)
# if trace['sign_status'] == 'SIGN':
# erp.mark_received(ORDER_ID, sign_time=trace['sign_time'])
except Exception as e:
print(f"❌ {e}")
三、1688快递公司码(LogisticsCode)常见值
快递 logisticsCode 说明
圆通 YTO 最常用
申通 STO —
中通 ZTO —
韵达 YD —
顺丰 SF 需买家寄付/月结
京东 JD —
邮政EMS EMS —
💡 避坑:1688返回的 logisticsCode 是平台内部简码,直接传给 trace.get 即可,不要自己映射汉字。
四、ERP侧同步策略建议
┌──────────────┐ 每30分钟轮询已发货未签收订单
│ 1688 已发货单│ ──────────────────────────────▶
└──────────────┘ │
get_order_logistics()
get_trace()
│
┌───────────▼──────────┐
│ ERP出库单状态更新 │
│ • 运输中 → 显示轨迹 │
│ • SIGN → 标记已签收 │
│ • 异常节点 → 告警 │
└──────────────────────┘
• 轮询频率:已发货未签收订单每30min查一次,签收后停止轮询
• 失败重试:物流接口偶发超时,指数退避重试3次
• 轨迹去重:按 (bill_no, time, desc) 去重存储,避免重复写状态变更
五、高频避坑点
问题 原因 解决
返回空物流 订单未发货/waitsellersend状态 先判断订单status,仅查已发货
companyCode无效 自己手填汉字快递名 必须用1688返回的logisticsCode
轨迹长期不更新 快递公司未回传 正常,按sign_status判断是否最终签收即可
限流429 QPS过高 单应用 sleep≥0.2s 或令牌桶 QPS≤10
六、面试/方案一句话
1688物流对接 = 用采购单ID调 logistics.trade.ship 拿运单号 → 调 logistics.trace.get 拉轨迹 → 按 sign_status 回写ERP签收状态,注意只对已发货订单查询且用平台返回的 logisticsCode。
需要我把物流定时同步任务(APScheduler/Celery Beat)或ERP签收回写SQL模板补给你吗?