Skip to content

Tracking API ​

当你只需要向客户展示一票货件的当前状态时,调用匿名 Tracking API。请求参数只能是 tracking_number,不能用 package code、scan code 或内部 ID 替代。

调用接口 ​

方法路径用途
GET/public/tracking/{tracking_number}/读取 JSON 状态投影。
HEAD/public/tracking/{tracking_number}/只检查状态码和响应头,不读取 body。

完整 URL 是 https://mexpress.nz/api/v1/public/tracking/{tracking_number}/。请将 tracking number 编码为一个 path segment。

HEAD 与 GET 共享成功、未找到和限流状态,但 HEAD 不返回响应体。

读取 200 响应 ​

200 OK 始终返回下列 required keys。timeline 可以是空数组;milestone、reason 和时间字段按契约可以为 null。

json
{
  "tracking_number": "MX123456789",
  "status": "out_for_delivery",
  "milestone": "out_for_delivery",
  "reason": null,
  "status_label": "Out for delivery",
  "package_count": 2,
  "package_summary": {
    "total": 2,
    "created": 0,
    "picked_up": 0,
    "in_warehouse": 0,
    "out_for_delivery": 2,
    "exception": 0,
    "returning": 0,
    "delivered": 0,
    "returned": 0,
    "cancelled": 0,
    "unresolved": 0
  },
  "projection_revision": 7,
  "occurred_at": "2026-07-02T03:04:05.000000Z",
  "timeline": []
}
字段契约
tracking_numberrequired string;请求的 shipment 标识。
statusrequired enum:created、in_transit、out_for_delivery、exception、delivered、returned、cancelled、completed。
milestonenullable enum:information_received、picked_up、at_transit_facility、out_for_delivery、delivery_exception、returning_to_sender、delivered、returned_to_sender、cancelled、delivered_and_returned、partially_delivered、partially_returned。
reasonnullable enum:recipient_unavailable、address_issue、access_issue、recipient_refused、damaged、other_delivery_issue。
status_labelrequired string;用于客户界面展示。
package_countrequired、非负 integer。
projection_revisionrequired、非负 integer;可用于缓存比较。
occurred_atnullable RFC 3339 date-time string;当前投影时间。
package_summaryrequired object;固定 11 个键,每个值为非负 integer。
timelinerequired array;可为空,事件按 oldest-first 排列。

package_summary 的固定键和顺序是:total、created、picked_up、in_warehouse、out_for_delivery、exception、returning、delivered、returned、cancelled、unresolved。不要添加键,也不要从计数推断具体 package 身份。

每个 timeline item 都有 required status、label 和 nullable occurred_at。status 只能是 created、picked_up、in_warehouse、loaded、out_for_delivery、delivery_attempted、delivered、exception、return_initiated、returned_to_sender、cancelled、completed;label 是 string,occurred_at 是 nullable date-time。空数组表示没有可用 timeline event。事件按 oldest-first 返回。

导入可运行示例 ​

下面的两个 region 是唯一示例源。它们明确处理 200、detail-only 404、429 和正整数 Retry-After,不把 token 或追踪数据放进浏览器 credential flow。

js
export async function fetchTracking(
  trackingNumber,
  { baseUrl = API_BASE_URL, fetchImpl = globalThis.fetch } = {},
) {
  const response = await fetchImpl(
    endpoint(baseUrl, `/public/tracking/${encodeURIComponent(trackingNumber)}/`),
    {
      method: "GET",
      headers: { Accept: "application/json" },
    },
  );
  if (response.status === 200) return { kind: "ok", data: await json(response) };
  if (response.status === 404) return { kind: "not_found" };
  if (response.status === 429) {
    const retryAfter = Number.parseInt(header(response.headers, "Retry-After") ?? "", 10);
    if (!Number.isInteger(retryAfter) || retryAfter < 1) {
      throw new Error("Tracking 429 did not include a positive Retry-After.");
    }
    return { kind: "rate_limited", retryAfter, error: await json(response) };
  }
  throw new Error(`Tracking request failed with HTTP ${response.status}.`);
}
py
def fetch_tracking(tracking_number: str, *, client, base_url: str = API_BASE_URL):
    response = client.get(
        _url(base_url, f"/public/tracking/{quote(tracking_number, safe='')}/"),
        headers={"Accept": "application/json"},
    )
    if response.status_code == 200:
        return {"kind": "ok", "data": response.json()}
    if response.status_code == 404:
        return {"kind": "not_found"}
    if response.status_code == 429:
        raw_retry_after = _header(response, "Retry-After")
        try:
            retry_after = int(raw_retry_after or "")
        except ValueError as error:
            raise ValueError("Tracking 429 did not include a positive Retry-After.") from error
        if retry_after < 1:
            raise ValueError("Tracking 429 did not include a positive Retry-After.")
        return {"kind": "rate_limited", "retry_after": retry_after, "error": response.json()}
    raise RuntimeError(f"Tracking request failed with HTTP {response.status_code}.")

处理错误和轮询 ​

404 只有 detail-only 结果。对未知、隐藏、格式错误或其他不可用原因都显示同一个中性结果,不向客户泄露区别。

429 的 JSON body 是 detail: "Too many public tracking requests. Retry after the indicated delay." 与 code: "public_tracking_rate_limited",并带正整数 Retry-After header。至少等待该时长,再使用指数退避和随机 jitter。

匿名限额是每个 source IP 每秒 5 次、burst 30 次,另有共享服务限额。只在客户界面需要刷新时轮询;单个 tracking number 每分钟一次是保守默认值。后端需要持续更新时优先使用 Webhooks。

公开投影不会包含收件人联系方式或地址、package scan code、POD media、精确设备位置、driver identity、计费、raw scan evidence 或内部 audit/risk 字段。

验收 ​

  • [ ] GET 只展示本页列出的安全字段。
  • [ ] 只在不需要 body 时使用 HEAD。
  • [ ] 404 在客户界面中只有一个中性结果。
  • [ ] 429 遵守正整数 Retry-After,再执行退避与 jitter。
  • [ ] 轮询没有 tight loop,并在合适时改用 Webhooks。

继续阅读 Webhooks 获取事件更新,或阅读 POD API 获取认证后的 POD evidence。

M Express 服务端集成指南