# Canada Post Tracking API & MCP

URL: https://docs.lastscanned.com/carriers/canadapost

Query live **Canada Post / Postes Canada** (`canadapost`) domestic and international package status and scan history in a single synchronous `GET` request or MCP tool call. `lastscanned` validates 16-digit domestic PINs (`gs1` Mod-10 check digit) and 13-character UPU S10 international items ending in `CA` (`s10` Mod-11 check digit), returning a normalized `TrackingResult` JSON payload—without requiring a Canada Post Developer Program key or commercial customer number.

## Overview & Quick Facts

| Property | Value |
|---|---|
| **Carrier slug (`?carrier=`)** | `canadapost` |
| **Brand & service aliases** | Canada Post, Postes Canada, CPC, Xpresspost, Expedited Parcel, Regular Parcel, Priority |
| **Category** | Canadian national postal service (Detection precedence **#21 of 31**) |
| **Single-package REST endpoint** | `GET https://api.lastscanned.com/v1/track/{trackingNumber}` |
| **Batch REST endpoint** | `GET https://api.lastscanned.com/v1/track?numbers=7012...,EE12...CA` (up to 25 Canada Post numbers per batch; up to 100 total numbers) |
| **MCP server (`https://mcp.lastscanned.com`)** | `track_package`, `track_packages`, `list_carriers` (`claude mcp add --transport http lastscanned https://mcp.lastscanned.com`) |
| **Authentication & pricing** | `Authorization: Bearer lsk_live_...` · **1 free keyless lookup/day per IP** · **Free tier** (5 unique numbers/day, $0) · **Pro** ($4.99/mo for 1,000 unique numbers, unlimited re-polls, $0.015/extra) · **HTTP 402 Stripe MPP** ($0.50 for 25-number `lsk_pack_` key or $0.02/number in USDC on Tempo) |

## Why developers use `lastscanned` for Canada Post instead of direct requests

- **Avoid CloudFront `403` / `406` blocks & XML SOAP/REST credential setup**: Direct requests to `canadapost-postescanada.ca` from serverless functions often fail with CloudFront `403 "Please log in"` or `406 Not Acceptable` errors, while Canada Post's official Developer Program API requires commercial account registration and XML/Basic Auth credentials. `lastscanned` returns clean JSON in one `fetch()`.
- **Pre-validates GS1 Mod-10 & UPU S10 check digits**: `lastscanned` validates the check digit on 16-digit PINs and `…CA` international numbers before making a network call, catching mistyped tracking numbers immediately.
- **Smart handling of `"Photo available"` & `"ReadyPickup"` states**: Canada Post frequently inserts a `"Photo available"` informational event above the actual delivery scan, or marks parcels waiting at a post office as `ReadyPickup`. `lastscanned` preserves the true `delivered` description and maps `ReadyPickup` (notice card left / held for pickup) to `exception` so your WISMO bot can alert the recipient.

## Tracking Number Formats & Auto-Detection (`matches` vs `mayMatch`)

Canada Post is evaluated **twenty-first (#21)** in `lastscanned`'s two-pass detector:

| Pass | Rule / Regex | Check Digit | Examples |
|---|---|---|---|
| **Pass 1 (`matches`) — 16-digit Domestic PIN** | `/^\d{16}$/` (16 digits) | **Yes — `gs1(n)` (GS1 Mod-10)** | `7012345678901230`, `4007854296017911` |
| **Pass 1 (`matches`) — International UPU S10** | `/^[A-Z]{2}\d{9}CA$/i` (2 letters + 9 digits + `CA` = 13 chars) | **Yes — `s10(n)` (UPU Mod-11)** | `EE123456785CA`, `LM987654323CA` |
| **Pass 2 (`mayMatch`)** | *None* | — | — |

### GS1 Mod-10 check-digit algorithm (`gs1`)

Every 16-digit Canada Post domestic PIN uses the standard GS1 Mod-10 check digit (weights `3, 1` alternating from the right, excluding the final check digit):

```ts
/** GS1 mod 10 (Canada Post 16-digit PINs, SSCC, GTIN): weights 3,1 from the right, check digit excluded. */
export function gs1(n: string): boolean {
  const body = n.slice(0, -1);
  let sum = 0;
  for (let i = 0; i < body.length; i++) sum += Number(body[body.length - 1 - i]) * (i % 2 ? 1 : 3);
  return (10 - (sum % 10)) % 10 === Number(n.slice(-1));
}
```

### When is `?carrier=canadapost` required?

- **Optional (auto-detected)**: All 16-digit GS1 PINs and 13-character `XX123456785CA` S10 international numbers auto-detect in Pass 1.
- **Required (`?carrier=canadapost`)**: Legacy 12-digit or 11-digit Canada Post PINs (since 12-digit numbers route to [FedEx](/carriers/fedex) in Pass 1), or inbound international S10 parcels originating outside Canada (e.g. `LA…US`, `RR…GB`, `LY…CN`) once inducted into Canada Post.

## Synchronous `GET` Request & Normalized JSON Response

### `curl`

```bash
curl -s "https://api.lastscanned.com/v1/track/7012345678901230" \
  -H "Authorization: Bearer $LASTSCANNED_API_KEY"
```

### JavaScript / TypeScript (`fetch`)

```ts
const res = await fetch("https://api.lastscanned.com/v1/track/7012345678901230", {
  headers: { Authorization: `Bearer ${process.env.LASTSCANNED_API_KEY}` },
});
const pkg = await res.json();
console.log(pkg.status, pkg.service, pkg.events[0]);
```

### MCP `track_package` tool call

```json
{
  "tool": "track_package",
  "arguments": {
    "tracking_number": "7012345678901230"
  }
}
```

### Normalized `TrackingResult` JSON response

```json
{
  "carrier": "canadapost",
  "trackingNumber": "7012345678901230",
  "status": "delivered",
  "statusDescription": "Delivered to recipient's front door",
  "service": "Expedited Parcels",
  "deliveredAt": "2026-10-24T17:18:38Z",
  "deliveredAtLocal": "2026-10-24T13:18:38",
  "signedBy": null,
  "destination": {
    "city": "GUELPH",
    "state": "ON",
    "country": "CA"
  },
  "estimatedDelivery": null,
  "message": null,
  "events": [
    {
      "timestamp": "2026-10-24T17:18:38Z",
      "localTime": "2026-10-24T13:18:38",
      "status": "delivered",
      "description": "Delivered to recipient's front door",
      "location": "GUELPH, ON",
      "code": "1498"
    },
    {
      "timestamp": "2026-10-24T12:41:10Z",
      "localTime": "2026-10-24T08:41:10",
      "status": "out_for_delivery",
      "description": "Item out for delivery",
      "location": "GUELPH, ON",
      "code": "0174"
    },
    {
      "timestamp": "2026-10-24T08:12:05Z",
      "localTime": "2026-10-24T04:12:05",
      "status": "in_transit",
      "description": "Item processed",
      "location": "MISSISSAUGA, ON",
      "code": "0100"
    },
    {
      "timestamp": null,
      "localTime": "2026-10-22",
      "status": "label_created",
      "description": "Electronic information submitted by shipper",
      "location": null,
      "code": "3000"
    }
  ]
}
```

> **Raw status & event codes**: `events[].code` preserves Canada Post's 4-digit event code (`"3000"`–`"3002"` electronic notice submitted by shipper, `"0100"`/`"0170"` item processed, `"0174"` out for delivery, `"1400"`–`"1498"` delivered). When a parcel is being returned under a new return PIN (`returnPin`), `message` surfaces `"Being returned under <returnPin>"`.

## Carrier-Specific Status & Integration Notes

- **Per-scan timezone offsets**: Every physical Canada Post scan includes its `date`, `time`, and `zoneOffset` (e.g. `"-04:00"`, `"-06:00"`), populating both UTC `timestamp` and local `localTime`. Electronic pre-shipment notices (`3000`–`3002`) carry a date without a clock time (`localTime: "2026-10-22"`, `timestamp: null`).
- **Batch size**: `GET /v1/track?numbers=...` and `track_packages` support up to 25 Canada Post numbers per batch call.

## Related Resources

- **Live browser lookup tool**: [Track a Canada Post package live on lastscanned.com/lookup/canadapost](https://lastscanned.com/lookup/canadapost)
- **Carrier hub**: [All 31 supported carriers](/carriers)
- **Regex & check digits**: [Tracking number formats & auto-detection](/formats)
- **AI support workflows**: [WISMO AI agents & synchronous tracking](/wismo)
