# USPS Tracking API & MCP

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

Query live **United States Postal Service (USPS)** (`usps`) package status and scan history in a single synchronous `GET` request or MCP tool call. `lastscanned` detects 20–26 digit USPS IMpb barcodes (including `420` + ZIP routing prefixes) and UPU S10 international tracking numbers, returning a normalized `TrackingResult` JSON payload—without requiring a USPS Web Tools / COPRA developer registration or MID/CRID onboarding.

## Overview & Quick Facts

| Property | Value |
|---|---|
| **Carrier slug (`?carrier=`)** | `usps` |
| **Brand & service aliases** | United States Postal Service, USPS Ground Advantage, Priority Mail, Priority Mail Express, First-Class Package International, Certified Mail, Media Mail |
| **Category** | US national postal service (Detection precedence **#2 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=9400...,9205...` (up to 35 numbers per carrier in a 100-number call) |
| **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 USPS instead of direct requests

- **Avoid Akamai challenge pages & `403` blocks from serverless backends**: Direct requests to `tools.usps.com` from Cloudflare Workers, Vercel, or AWS Lambda frequently receive blank Akamai challenge responses or HTTP `403` blocks. Calling `GET https://api.lastscanned.com/v1/track/:number` from your serverless function returns structured JSON in one `fetch()`.
- **No USPS COPRA / Web Tools migration friction**: USPS retired legacy Web Tools XML endpoints in favor of the v3 OAuth API requiring Business Customer Gateway accounts, Mailer IDs (MID), and Customer Registration IDs (CRID). `lastscanned` requires zero USPS account onboarding.
- **Cross-border & consolidator handoff target**: Cross-border parcels from [Yanwen](/carriers/yanwen), [4PX](/carriers/4px), [Cainiao](/carriers/cainiao), [YunExpress](/carriers/yunexpress), [CNE Express](/carriers/cne), and [SunYou](/carriers/sunyou)—plus domestic consolidators like [OSM Worldwide](/carriers/osm), [Pitney Bowes](/carriers/pitneybowes), and DHL eCommerce—induct into USPS under `420` + ZIP + `92/93…` barcodes that `lastscanned` tracks natively.

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

USPS is evaluated **second (#2)** in `lastscanned`'s two-pass detector:

| Pass | Rule / Regex | Check Digit | Examples |
|---|---|---|---|
| **Pass 1 (`matches`) — Domestic IMpb** | `/^(420\d{5})?9[2345]\d{18,24}$/` (20–26 digits starting `92`, `93`, `94`, or `95`, optionally preceded by `420` + 5-digit ZIP) | — | `9400111899223847192011`, `420787019261290312839104827163` |
| **Pass 1 (`matches`) — UPU S10** | `/^[A-Z]{2}\d{9}US$/i` (2 letters + 9 digits + `US`) | **Yes — `s10(n)` (Mod-11)** | `EA123456785US`, `LK987654323US` |
| **Pass 2 (`mayMatch`)** | *None* | — | — |

### UPU `s10` Mod-11 check-digit algorithm

For 13-character international numbers ending in `US`, `lastscanned` validates the 9th digit (index 10) against the first 8 serial digits using UPU S10 weights `[8, 6, 4, 2, 3, 5, 9, 7]`:

```ts
/** UPU S10 (EA123456785US): weights 8,6,4,2,3,5,9,7 over the 8 serial digits, mod 11. */
export function s10(n: string): boolean {
  const d = n.slice(2, 11);
  const w = [8, 6, 4, 2, 3, 5, 9, 7];
  const c = 11 - (w.reduce((s, x, i) => s + x * Number(d[i]), 0) % 11);
  return (c === 10 ? 0 : c === 11 ? 5 : c) === Number(d[8]);
}
```

### Disambiguating 22-digit USPS vs FedEx & Consolidator Labels (`?carrier=osm`, `?carrier=pitneybowes`)

- **No collision with FedEx 22-digit SmartPost**: USPS 22-digit IMpb labels start with `92`, `93`, `94`, or `95`, whereas FedEx 22-digit numbers start with `96` (`/^96\d{20}$/`). Both auto-detect without `?carrier=`.
- **Resolving `"USPS Currently Awaiting Package"` on `92…` consolidator labels**: When a parcel is shipped through **[OSM Worldwide](/carriers/osm)** or **[Pitney Bowes](/carriers/pitneybowes)** using a `92…` (or `420` + ZIP + `92…`) USPS barcode, USPS only displays *"Pre-Shipment / Picked Up by Shipping Partner, USPS Awaiting Item"* until the consolidator drops the bag at the local USPS facility days later. Pass **`?carrier=osm`** or **`?carrier=pitneybowes`** with that same `92…` number to retrieve the consolidator's live middle-mile sort scans!
- **When `?carrier=usps` is required**: Legacy 20-digit Certified Mail labels starting with `70…`, 13-character inbound international S10 parcels originating outside the US (e.g. `RR…GB`, `CP…CN` once handed to USPS), or 10-digit Global Express Guaranteed numbers.

## Synchronous `GET` Request & Normalized JSON Response

### `curl`

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

### JavaScript / TypeScript (`fetch`)

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

### MCP `track_package` tool call

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

### Normalized `TrackingResult` JSON response

```json
{
  "carrier": "usps",
  "trackingNumber": "9400111899223847192011",
  "status": "delivered",
  "statusDescription": "Delivered, In/At Mailbox",
  "service": "USPS Ground Advantage",
  "deliveredAt": "2026-10-24T19:12:00Z",
  "deliveredAtLocal": "2026-10-24T14:12:00",
  "signedBy": null,
  "destination": {
    "city": "CHICAGO",
    "state": "IL",
    "country": "US"
  },
  "estimatedDelivery": null,
  "message": "Your item was delivered in or at the mailbox at 2:12 pm on October 24, 2026 in CHICAGO, IL 60614.",
  "events": [
    {
      "timestamp": "2026-10-24T19:12:00Z",
      "localTime": "2026-10-24T14:12:00",
      "status": "delivered",
      "description": "Delivered, In/At Mailbox",
      "location": "CHICAGO, IL 60614",
      "code": null
    },
    {
      "timestamp": "2026-10-24T11:10:00Z",
      "localTime": "2026-10-24T06:10:00",
      "status": "out_for_delivery",
      "description": "Out for Delivery",
      "location": "CHICAGO, IL 60614",
      "code": null
    },
    {
      "timestamp": "2026-10-24T10:59:00Z",
      "localTime": "2026-10-24T05:59:00",
      "status": "in_transit",
      "description": "Arrived at Post Office",
      "location": "CHICAGO, IL 60614",
      "code": null
    },
    {
      "timestamp": "2026-10-22T03:45:00Z",
      "localTime": "2026-10-21T22:45:00",
      "status": "label_created",
      "description": "Shipping Label Created, USPS Awaiting Item",
      "location": "DALLAS, TX 75201",
      "code": null
    }
  ]
}
```

> **Raw status & scan fields**: USPS does not expose numeric event codes on public tracking timelines, so `events[].code` is `null` while `statusDescription`, `message`, and `events[].description` preserve the exact USPS scan wording (e.g. `"Delivered, In/At Mailbox"`, `"Arrived at USPS Regional Facility"`).

## Carrier-Specific Status & Integration Notes

- **Timestamps (`localTime` and `timestamp`)**: USPS prints wall-clock times without an explicit UTC offset on each scan row (and date-only strings like `"2026-10-22"` on transit facility milestones without a clock time). `lastscanned` preserves the exact printed clock in `localTime` and converts full date-time scans to UTC in `timestamp`.
- **Top-level `message` summary**: USPS provides a human-readable summary sentence for the latest scan (for example, *"Your item was delivered in or at the mailbox at 2:12 pm…"* or *"Your package will arrive later than expected, but is still on its way."*), which is returned in `message`.
- **Up to 35 USPS numbers per batch**: `GET /v1/track?numbers=...` and the MCP `track_packages` tool accept up to 35 USPS tracking numbers in a single batch call.

## Related Resources

- **Live browser lookup tool**: [Track a USPS package live on lastscanned.com/lookup/usps](https://lastscanned.com/lookup/usps)
- **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)
