Carriers

UPS Tracking API & MCP

Query live UPS (ups) package status and full scan history in a single synchronous GET request or MCP tool call. lastscanned validates UPS 1Z Mod-10 check digits upfront and returns a normalized TrackingResult JSON payload—without requiring a UPS Developer Portal OAuth application, shipper account number, or webhook receiver.

Overview & Quick Facts

PropertyValue
Carrier slug (?carrier=)ups
Brand & service aliasesUnited Parcel Service, UPS Ground, UPS Next Day Air, UPS 2nd Day Air, UPS 3 Day Select, UPS SurePost, UPS Worldwide Express / Saver / Expedited
CategoryNational & international parcel carrier (Detection precedence #1 of 31)
Single-package REST endpointGET https://api.lastscanned.com/v1/track/{trackingNumber}
Batch REST endpointGET https://api.lastscanned.com/v1/track?numbers=1Z...,1Z... (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 & pricingAuthorization: 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 UPS instead of direct requests

  • Avoid Akamai Bot Manager 403 blocks from serverless & edge IPs: If you try to call webapis.ups.com/track/api/Track/GetStatus directly from a Cloudflare Worker, Vercel function, or AWS Lambda, Akamai rejects datacenter TLS fingerprints and IPs, and requires paired GetCSRFToken / X-XSRF-TOKEN-ST session cookies. Calling GET https://api.lastscanned.com/v1/track/:number from your Worker or Lambda returns clean JSON in one fetch().
  • Skip UPS Developer Kit OAuth 2.0 & shipper account linking: Official UPS Track API credentials require registering an enterprise app, linking a 6-character UPS shipper account, and refreshing short-lived OAuth tokens. lastscanned works immediately with a single Bearer key (or keyless via HTTP 402).
  • Synchronous GET—no webhook registration: Unlike AfterShip, Ship24, or EasyPost where you must POST a tracker first and wait for an async webhook, GET /v1/track/:number returns the live UPS scan timeline inline.

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

UPS is evaluated first (#1) in lastscanned's two-pass detector:

PassRule / RegexCheck DigitExamples
Pass 1 (matches)/^1Z[0-9A-Z]{16}$/i (18 characters starting with 1Z)Yes — ups1Z(n) (Mod-10)1Z999AA10123456784, 1Z5868Y60389226681
Pass 2 (mayMatch)None——

ups1Z Mod-10 check-digit algorithm

Every 18-character 1Z number consists of 1Z + 6-character shipper account + 2-digit service code + 7-digit package identifier + 1 Mod-10 check digit. lastscanned validates the check digit before making any network call so typos fail fast with 400 unknown_carrier:

/** UPS 1Z: letters count as (char code - 63) mod 10, odd positions doubled, mod 10. */
export function ups1Z(n: string): boolean {
  const body = n.slice(2, -1).toUpperCase();
  let sum = 0;
  for (let i = 0; i < body.length; i++) {
    const ch = body[i];
    const v = ch >= "0" && ch <= "9" ? Number(ch) : (ch.charCodeAt(0) - 63) % 10;
    sum += i % 2 ? v * 2 : v;
  }
  return (10 - (sum % 10)) % 10 === Number(n.slice(-1));
}

When is ?carrier=ups optional vs required?

  • Optional (auto-detected): All standard 18-character 1Z tracking numbers with a valid Mod-10 check digit. No other carrier uses the 1Z prefix.
  • Required (?carrier=ups): Non-1Z UPS formats such as 9-digit or 12-digit UPS Freight / TForce PRO numbers, UPS InfoNotice numbers, or UPS Mail Innovations labels that do not use a USPS 92…/93… IMpb barcode.

Synchronous GET Request & Normalized JSON Response

curl

curl -s "https://api.lastscanned.com/v1/track/1Z999AA10123456784" \
  -H "Authorization: Bearer $LASTSCANNED_API_KEY"

JavaScript / TypeScript (fetch)

const res = await fetch("https://api.lastscanned.com/v1/track/1Z999AA10123456784", {
  headers: { Authorization: `Bearer ${process.env.LASTSCANNED_API_KEY}` },
});
const pkg = await res.json();
console.log(pkg.status, pkg.service, pkg.deliveredAt);

MCP track_package tool call

{
  "tool": "track_package",
  "arguments": {
    "tracking_number": "1Z999AA10123456784"
  }
}

Normalized TrackingResult JSON response

{
  "carrier": "ups",
  "trackingNumber": "1Z999AA10123456784",
  "status": "delivered",
  "statusDescription": "Delivered",
  "service": "UPS Ground",
  "deliveredAt": "2026-10-23T20:17:14Z",
  "deliveredAtLocal": "2026-10-23T15:17:00",
  "signedBy": "FRONT DOOR",
  "destination": {
    "city": "AUSTIN",
    "state": "TX",
    "country": "US"
  },
  "events": [
    {
      "timestamp": "2026-10-23T20:17:14Z",
      "localTime": "2026-10-23T15:17:00",
      "status": "delivered",
      "description": "Delivered",
      "location": "AUSTIN, TX, US",
      "code": "KB"
    },
    {
      "timestamp": "2026-10-23T13:42:08Z",
      "localTime": "2026-10-23T08:42:00",
      "status": "out_for_delivery",
      "description": "Out For Delivery Today",
      "location": "AUSTIN, TX, US",
      "code": "OT"
    },
    {
      "timestamp": "2026-10-23T09:15:30Z",
      "localTime": "2026-10-23T04:15:00",
      "status": "in_transit",
      "description": "Arrived at Facility",
      "location": "AUSTIN, TX, US",
      "code": "AR"
    },
    {
      "timestamp": "2026-10-21T22:04:11Z",
      "localTime": "2026-10-21T18:04:00",
      "status": "label_created",
      "description": "Shipper created a label, UPS has not received the package yet.",
      "location": "US",
      "code": "MP"
    }
  ]
}

Raw status & scan codes: Top-level statusDescription preserves UPS's exact status wording (e.g. "Delivered", "On the Way"), while each entry in events[] includes normalized status, raw description, and UPS's 2-character activity code (e.g. "KB", "OT", "AR", "DP", "MP").

Carrier-Specific Status & Integration Notes

  • Exact UTC (timestamp) and scan-local (localTime) clocks: UPS reports both gmtDate/gmtTime (down to the second in UTC, e.g. "2026-10-23T20:17:14Z") and the scan location's wall-clock time (date/time, normalized to "2026-10-23T15:17:00"). Both timestamp and localTime are populated on every scan.
  • UPS status type mapping: UPS package status types map deterministically to the 7 normalized statuses: M → label_created, I → in_transit, O → out_for_delivery, D → delivered, X → exception, and RS → returned.
  • Service level & proof of delivery: service returns the specific UPS service product (such as "UPS Ground", "UPS Next Day Air", or "UPS 2nd Day Air"), and signedBy captures the receiver name or release location (receivedBy) when available.

On this page