pending
15A label exists, but the carrier does not have the package yet.
- USPS
Shipping Label Created, USPS Awaiting Item
,Pre-Shipment Info Sent to USPS
and 7 more- UPS
- MP MV OC
- FedEx
- OC PX LO
- DHL
- Text such as
shipment information received
ship·ment track·ing API n. 1A free HTTP API that tracks USPS, UPS, FedEx and DHL packages. 2One endpoint that answers all four carriers with the same nine statuses.
Each line is one entry in the table this API reads from: 92 USPS phrases, 38 UPS activity codes and 29 FedEx status codes. DHL has no fixed list, so its text is matched by rule. The lines that end at delivered are drawn in green.
Anything the table does not cover comes back as unknown, with the carrier’s own words attached.
Every scan comes back twice: once in the carrier’s own words, and once as one of the nine. Your code only has to read the second.
| When | Where | UPS wrote | becomes | You get |
|---|---|---|---|---|
| Nov 3, 4:12 PM | Louisville, KY, US | Shipper created a label, UPS has not received the package yet. | pending | |
| Nov 4, 7:41 PM | Louisville, KY, US | Departed from Facility | in_transit | |
| Nov 5, 3:27 AM | Hodgkins, IL, US | Arrived at Facility | in_transit | |
| Nov 5, 8:52 AM | Chicago, IL, US | Out For Delivery Today | out_for_delivery | |
| Nov 5, 1:36 PM | Chicago, IL, US | DELIVERED | delivered |
Nine statuses cover everything a package can be doing. Under each one is what the carriers say when they mean it.
A label exists, but the carrier does not have the package yet.
Shipping Label Created, USPS Awaiting Item,
Pre-Shipment Info Sent to USPSand 7 more
shipment information received
Moving through the carrier’s network. Also a missed delivery that can still be picked up or redelivered.
In Transit to Next Facility,
Arrived at USPS Regional Facility,
Notice Leftand 54 more
departed,
arrived,
customs
On the vehicle for the final delivery.
Out for Delivery,
Preparing for Delivery
with delivery courier
Handed over at the destination.
Delivered, In/At Mailbox,
Delivered, Front Door/Porchand 6 more
delivered
The delivery could not be completed.
Undeliverable as Addressed,
No Such Number,
Refusedand 8 more
unsuccessful
Going back to the sender, or already back.
Return to Sender,
Returned to Sender,
Intercepted
returned to shipper
The shipment was cancelled before delivery.
The carrier reports the package missing.
Missing Mail Search Request Initiated,
Sent to Mail Recovery Center
The carrier said something the table does not cover yet.
The request is the same for all four carriers. Only the carrier id changes.
carrier and its trackingNumbers, an array even for one.status. It is always one of the nine.events for every scan, oldest first, each with the carrier’s own description.results[] | One entry for each number, in the order you sent them. |
status | One of the nine. The package’s latest state. |
events[].timestamp | ISO 8601. USPS times are in UTC; the other carriers keep the local offset. |
events[].status | One of the nine, for that scan. |
events[].description | The carrier’s own words. |
events[].location | Where it was scanned, when the carrier says. |
estimatedDelivery | When the carrier gives one. |
error | In place of a status, when that one number has no answer. |
curl https://api.shipmenttrackingapi.com/v1/track \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"carrier":"ups","trackingNumbers":["YOUR_TRACKING_NUMBER"]}' const res = await fetch("https://api.shipmenttrackingapi.com/v1/track", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ carrier: "ups", trackingNumbers: ["YOUR_TRACKING_NUMBER"] }),
});
const body = await res.json();
if (!body.success) throw new Error(body.error.code);
for (const r of body.data.results) {
console.log(r.trackingNumber, r.status ?? r.error.code);
} import requests
res = requests.post(
"https://api.shipmenttrackingapi.com/v1/track",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"carrier": "ups", "trackingNumbers": ["YOUR_TRACKING_NUMBER"]},
)
body = res.json()
if not body["success"]:
raise SystemExit(body["error"]["code"])
for r in body["data"]["results"]:
print(r["trackingNumber"], r.get("status") or r["error"]["code"]) USPS writes sentences. UPS and FedEx send two-character codes. DHL sends a code and free text. Your code never sees the difference.
In Transit to Next Facility / Arrived at USPS Regional Facility / Out for Delivery / Delivered, In/At Mailbox / Notice Left / Delivery Delay, Animal Interference / Return to Sender
A number looks like 9400 #### #### #### #### ##KB DL DM FS 9E OT AR DP YP OR IT OF DS LC RD IP AL ZO 08 18 19 PC PU XD DR PT MP MV OC DE SR NR NA RS RT VO CA VP
A number looks like 1Z ### ### ## #### ####DL OD AR DP IT AO AE AD AF CH CC CD IP IX OF RR HL PU SF FD OC PX LO AS DY DE SE CA RS
A number looks like #### #### ####delivered
/ with delivery courier
/ returned to shipper
/ shipment information
/ customs
/ departed
| Price | $0 | No credit card and no trial clock. |
| Lookups per account | 1,000 a month | One request is one lookup. Resets on the 1st, UTC. |
| USPS packages, at most | 35,000 a month | A USPS request carries up to 35 numbers and still counts as one lookup. |
| Lookups per IP address | 1,000 a month | Counted across every key that calls from that address. |
| Requests per account | 30 a minute | A 429 tells you when to retry. |
| Numbers per request | 1 to 35 | USPS takes 35 at once; UPS, FedEx and DHL take 1. |
| Keys per account | 1 | Sign in with Google to get it. |
| What counts | Answers | A request counts once, when any number in it returns tracking data or a clear “not found”. Errors are free. |