用 Python 对接快递查询接口:requests 完整示例
2026-07-26 · 阅读约 7 分钟
项目是 Python 写的(Django、Flask、FastAPI 后台,或者纯脚本、定时任务),
想加一个快递查询接口其实只差一个 requests.post。
本文全部代码都能直接跑,带你走完:发第一个请求 → 解析统一状态码 → 批量查询 →
处理失败条目 → 加重试 → 判断是否签收。
一、准备工作
先注册(邮箱即可,无需企业认证),到控制台生成一对密钥:
pk_ 开头的 API Key 与 sk_ 开头的 Secret Key。
然后装好 requests:
pip install requests 不想先注册?首页的在线演示用公开测试密钥就能试跑一遍。
二、发起第一个请求
接口是 POST /v1/tracking/trace,认证放在请求头里:
Authorization: Bearer API_KEY:SECRET_KEY,两段密钥用英文冒号拼接。
import os
import requests
API_URL = "https://api.kuaidichaxunapi.com/v1/tracking/trace"
# 密钥从环境变量读取,切勿写死在源码里
API_KEY = os.environ["KUAIDI_KEY"] # pk_ 开头
SECRET_KEY = os.environ["KUAIDI_SECRET"] # sk_ 开头
headers = {
"Authorization": f"Bearer {API_KEY}:{SECRET_KEY}",
"Content-Type": "application/json",
}
payload = {"items": [{"courierCode": "cainiao", "trackingNumber": "CGD0000123456"}]}
resp = requests.post(API_URL, headers=headers, json=payload, timeout=10)
resp.raise_for_status()
body = resp.json() items 是数组,一次最多 10 个。用 requests 的 json= 参数会自动序列化并带上
JSON 的 Content-Type,比手动 json.dumps 省事。
三、解析返回:状态与轨迹
返回是统一信封:body["data"]["results"] 与查询顺序一一对应。
每条先看 success,成功再读 data:
tiao = body["data"]["results"][0]
if tiao["success"]:
d = tiao["data"]
print(d["trackingNumber"], d["deliveryStatusText"]) # 例如:已签收
print("是否签收:", d["isDelivered"])
# progresses 最新在前,逐条打印轨迹
for p in d["progresses"]:
print(p["dateTime"], p["statusCode"], p["description"])
else:
err = tiao["error"]
print("查询失败:", err["code"], err["message"])
省心的地方在于 deliveryStatus 是归一后的状态码(共 12 种),
而 deliveryStatusText 已经是对应中文,可直接展示,无需自己写一张映射表。
轨迹里的每条 progresses[i] 含 dateTime、location、
status、statusCode、description。
四、批量查询与失败分支
把多个运单号塞进 items 一次查完。批量里某条失败不会让整个请求报错,
逐条判断 success,并根据错误码区分对待:
def chaxun(items):
resp = requests.post(API_URL, headers=headers, json={"items": items}, timeout=10)
resp.raise_for_status()
return resp.json()
danhao = [
{"courierCode": "cainiao", "trackingNumber": "CGD0000123456"},
{"courierCode": "sf", "trackingNumber": "SF9900001234567"},
]
body = chaxun(danhao)
print("汇总:", body["data"]["summary"]) # total / successful / failed / billable
for tiao in body["data"]["results"]:
if tiao["success"]:
d = tiao["data"]
print(d["trackingNumber"], "→", d["deliveryStatusText"])
else:
err = tiao["error"]
code = err["code"]
if code == "COURIER_PREPARING":
print(err["trackingNumber"], "该快递查询开发中,本次不计费")
elif code == "NOT_FOUND":
print(err["trackingNumber"], "暂无轨迹,稍后重试")
else:
print(err["trackingNumber"], "失败:", code)
两个错误码值得记住:NOT_FOUND 表示单号有效但暂无轨迹(新单常见),会计费;
COURIER_PREPARING 表示该公司查询功能仍在开发中,这类请求不消耗额度。
summary.billable 会告诉你这批实际计费了几条。
五、加上重试(指数退避)
网络抖动或偶发 5xx,用指数退避重试最稳妥——等待时间按 1、2、4 秒递增,避免瞬间打爆服务端:
import time
def chaxun_retry(items, max_retries=3):
for attempt in range(max_retries):
try:
resp = requests.post(API_URL, headers=headers,
json={"items": items}, timeout=10)
resp.raise_for_status()
return resp.json()
except requests.RequestException as e:
if attempt == max_retries - 1:
raise # 重试用尽,抛出
wait = 2 ** attempt # 1s, 2s, 4s… 指数退避
print(f"第 {attempt + 1} 次失败,{wait}s 后重试:", e)
time.sleep(wait)
提示:4xx(如 401 密钥错误、400 参数缺失)重试也没用,
raise_for_status() 会抛出,检查密钥和请求体即可;只对超时和 5xx 做退避。
六、判断「是否已签收」
做「待收货 / 已完成」分类时,用归一状态码判断最通用——不同快递公司的原始描述千差万别,
但终态都会归到 DELIVERED:
def yi_qianshou(tiao):
# 用归一后的状态码判断,跨快递公司通用
return tiao["success"] and tiao["data"]["deliveryStatus"] == "DELIVERED"
# 等价写法:直接用布尔字段 isDelivered
def yi_qianshou2(tiao):
return tiao["success"] and tiao["data"]["isDelivered"]
一旦某单 isDelivered 为真,它已到终态,就不必再查,能省下不少额度。
小结
- 认证是一个头:
Authorization: Bearer pk_xxx:sk_xxx,密钥放服务端环境变量。 - 批量逐条判断
success;失败看error.code,其中COURIER_PREPARING不计费。 - 只对超时 / 5xx 做指数退避;4xx 直接查问题。
- 签收判断认准
DELIVERED/isDelivered,终态即可停查。
完整字段与限制见接口文档,各家快递编码见快递公司页。 免费额度每月 10000 次,够绝大多数 Python 项目起步。