Tracking
One number
curl https://api.lastscanned.com/v1/track/1Z999AA10123456784 \
-H "Authorization: Bearer $LASTSCANNED_KEY"Returns a tracking result, or an error: 404 not_found when the carrier has no record, 400 unknown_carrier when no carrier's format matches.
Several numbers
GET /v1/track?numbers=a,b,c takes up to 100 numbers, at most 35 for any one carrier. Numbers from different carriers can be mixed; duplicates are removed.
curl "https://api.lastscanned.com/v1/track?numbers=1Z999AA10123456784,123456789012" \
-H "Authorization: Bearer $LASTSCANNED_KEY"{
"results": [
{ "trackingNumber": "1Z999AA10123456784", "result": { "carrier": "ups", "status": "in_transit", "events": [] } },
{ "trackingNumber": "123456789012", "error": { "code": "not_found", "message": "FedEx returned no track details for that number" } }
]
}- One entry per number, in the order sent, each with
result(same shape as a single lookup) orerror. One bad number never fails the others. - The call returns
200unless it is malformed (400), unauthorized (401) or over the daily limit (429). - The response comes back when every number has an answer. Each carrier's numbers are looked up together.
x-cache-hitsis how many numbers came from cache.
Carrier detection
The carrier is worked out from the number's format, and its check digit where the format has one, so a typo is turned away with unknown_carrier instead of being looked up. Formats are listed in Carriers.
One carrier per number. Detection picks exactly one carrier and never falls back to another. If that carrier has no record, the answer is not_found.
Pass carrier to choose the carrier yourself and skip detection:
curl "https://api.lastscanned.com/v1/track/123456789012?carrier=fedex" \
-H "Authorization: Bearer $LASTSCANNED_KEY"Use it when:
- the number is too generic to detect (OnTrac references, some UniUni and GOFO partner numbers, shippers' own numbers on Veho, CDL shipper references, most AxleHire codes);
- two carriers share a format and detection picked the wrong one (20-digit DHL Paket numbers not starting
0034043go to FedEx; Cirro'sGFUS…numbers go to GOFO).
On the batch route, carrier applies to every number. Carrier ids come from GET /v1/carriers.
The response
| Field | |
|---|---|
carrier | Carrier id, e.g. ups. |
trackingNumber | Upper-cased, trimmed. |
status | label_created, in_transit, out_for_delivery, delivered, exception, returned or unknown. |
statusDescription | The carrier's wording of the status. |
service | e.g. UPS Ground. Can be null. |
deliveredAt | Delivery time in UTC, or null. |
deliveredAtLocal | Delivery time as the carrier printed it, no zone. |
signedBy | Can be null. |
destination | { city, state, country }, any of them null; or null. |
estimatedDelivery | Expected delivery as local time with no zone, often a date only. null or absent if the carrier gives none. |
estimatedDeliveryAt | The same moment in UTC, only when the carrier states its zone (FedEx, DHL). |
message | The carrier's latest-update sentence, e.g. why a delivery is late. Cross-border carriers name the handoff carrier and its number here (Handed to USPS as 4207…). |
events | Scans, newest first. |
Each event has status, description, location (or null), code (the carrier's event code; null for USPS) and two times:
timestamp: UTC, ISO 8601 withZ. Use this one. It isnullwhen the carrier gave no time of day or no zone to convert from.localTime: the time as the carrier printed it, no zone (2026-08-07T16:17:00). A date alone means no time of day was given. Alwaysnullfor Amazon.
Where timestamp comes from:
- UPS, Amazon: UTC from the carrier.
- FedEx, OnTrac, UniUni, GOFO, Veho: the carrier gives a zone with each scan, so the conversion is exact. A UniUni "order received" scan has no zone and no
timestamp. - DHL: Express usually gives an offset. eCommerce and Parcel often don't, and
timestampis thennull. - USPS: prints no zone.
timestampassumes the zone of the location the page was loaded from; USPS doesn't state it. - Cirro, YunExpress: only the newest scan has a
timestamp.