Blog

Track 35 USPS packages in one API request.

batch n. 1Several tracking numbers sent in one request. 2USPS takes up to 35; UPS, FedEx and DHL take 1.

In this noteShowHide
  1. 1The batch request
  2. 2The response
  3. 3When one number fails
  4. 4How a batch is counted
  5. 5Tracking more than 35 packages
  6. 6How long a batch takes
  7. 7UPS, FedEx and DHL

To track multiple USPS packages from code, put up to 35 tracking numbers in one API request. It counts as one lookup. A free key comes with 1,000 lookups a month: 1,000 packages if you send them one at a time, up to 35,000 if you send 35 at a time.

1The batch request

Send the carrier and an array of tracking numbers. The array is required even for one number, so the code that sends one is the code that sends 35.

RequestPOST /v1/track
curl https://api.shipmenttrackingapi.com/v1/track \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "carrier": "usps",
    "trackingNumbers": [
      "YOUR_TRACKING_NUMBER_1",
      "YOUR_TRACKING_NUMBER_2",
      "YOUR_TRACKING_NUMBER_3"
    ]
  }'
This request needs a key, and the answer takes about 30 seconds.Get a free key

A batch has four rules: one carrier per request, at most 35 numbers, no number twice, and every number 6 to 40 letters and digits. USPS prints its numbers with spaces, so strip them first. Break one and the whole request is rejected with a 400. The message names the rule and points to the number at fault.

You can also send a batch without writing code: paste your key and up to 35 USPS numbers, one per line, into the Playground.

2The response

results has one entry for each number, in the order you sent them. An entry is either tracking data or an error for that one number. The HTTP status is 200 either way.

Response200 OK
{
"success": true,
"data": {
"carrier": "usps",
"results": [
{
"trackingNumber": "YOUR_TRACKING_NUMBER_1",
"status": "delivered",
"events": [ … every scan … ]
},
{
"trackingNumber": "YOUR_TRACKING_NUMBER_2",
"status": "in_transit",
"events": [ … every scan … ]
},
{
"trackingNumber": "YOUR_TRACKING_NUMBER_3",
"error": {
"code": "TRACKING_NOT_FOUND",
"message": "No tracking information found for this tracking number"
}
}
]
}
}

An example, not a real shipment. The scans are left out.

Because the order is kept, results[i] is the answer for trackingNumbers[i]. Each number comes back in uppercase, with leading and trailing spaces removed.

3When one number fails

One number without an answer does not fail the others. Its entry carries an error in place of a status. Of these errors, only TRACKING_NOT_FOUND makes the request count, the same as tracking data does.

CodeWhat it means
TRACKING_NOT_FOUNDUSPS has no record of that number.
INVALID_TRACKING_NUMBERNot a valid USPS tracking number.
CARRIER_ERRORUSPS did not answer for that number. Send it again.
Caution

INVALID_TRACKING_NUMBER can also reject the whole request. If a number is not shaped like a USPS number, the API answers 400, nothing is counted, and the message lists up to five of the numbers to fix.

4How a batch is counted

A request counts as one lookup when at least one number in it comes back with tracking data or with TRACKING_NOT_FOUND. A request where every entry is INVALID_TRACKING_NUMBER or CARRIER_ERROR is not counted. Neither is a request that was rejected with a 400, 429 or 503.

Lookups per account1,000 a monthOne request is one lookup. Resets on the 1st, UTC.
Lookups per IP address1,000 a monthCounted across every key that calls from that address.
Numbers per USPS request35UPS, FedEx and DHL take 1.
USPS packages, at most35,000 a month1,000 requests with 35 numbers in each.
Requests per account30 a minuteA 429 tells you when to retry.

The response header X-RateLimit-Remaining-Month says how many lookups are left after each request.

5Tracking more than 35 packages

Split the list into groups of 35 and send them one after another. Each request takes about 30 seconds, so a loop sends about two a minute, well under the limit of 30 requests a minute. Requests sent at the same moment are more likely to come back 503. If a group does come back 503, send it again: it was not counted.

// Node 18 or later, saved as track.mjs
const API = "https://api.shipmenttrackingapi.com/v1";
const headers = {
  Authorization: "Bearer YOUR_API_KEY",
  "Content-Type": "application/json",
};
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

// Your USPS numbers. Remove spaces and repeats: the API rejects both.
const list = ["YOUR_TRACKING_NUMBER_1", "YOUR_TRACKING_NUMBER_2"];
const clean = list.map((n) => n.replace(/[^0-9A-Za-z]/g, "").toUpperCase());
const numbers = [...new Set(clean)];

// How many numbers one USPS request takes. This call needs no key.
const { data } = await (await fetch(`${API}/carriers`)).json();
const size = data.carriers.find((c) => c.id === "usps").maxTrackingNumbers;

const results = [];
for (let i = 0; i < numbers.length; i += size) {
  const group = numbers.slice(i, i + size);
  let res;
  for (let attempt = 1; attempt <= 3; attempt++) {
    res = await fetch(`${API}/track`, {
      method: "POST",
      headers,
      body: JSON.stringify({ carrier: "usps", trackingNumbers: group }),
      signal: AbortSignal.timeout(60_000),
    });
    if (res.status !== 503) break; // a 503 was not counted
    await sleep(5_000);
  }
  const body = await res.json();
  if (!body.success) throw new Error(body.error.code);
  results.push(...body.data.results);
}

results ends up in the same order as numbers, one entry each.

6How long a batch takes

A batch is not instant. In our tests a USPS request took 24 to 28 seconds, whether it carried 2 numbers or 35. Some take longer.

A request that runs too long comes back 503, is not counted, and can be sent again. Set your HTTP client’s timeout to 60 seconds: a client that gives up sooner can miss a result that was still counted.

7UPS, FedEx and DHL

UPS, FedEx and DHL take one number per request. Your code does not have to hard-code that: GET /v1/carriers returns the limit for each carrier, and it needs no key.

RequestGET /v1/carriers
curl https://api.shipmenttrackingapi.com/v1/carriers
Response200 OK
{
"success": true,
"data": {
"carriers": [
{
"id": "usps",
"name": "USPS",
"website": "https://www.usps.com",
"maxTrackingNumbers": 35
},
… ups, fedex and dhl, each with 1 …
]
}
}

The other three entries are shortened.

See alsoAll notes
StartAbout a minute

Sign in with Google. Your key is ready in seconds.