If you’ve ever looked at an international tracking number like LX123456789CN or EE987654321US and wondered why every postal service in the world produces numbers in the same shape, the answer is the UPU S10 standard.
This is the format used by every member country of the Universal Postal Union for cross-border letters and parcels. Once you understand it, you can validate tracking numbers, identify the originating country, and even spot typos before sending an API request.
This guide breaks down the format down to the character, including the often-skipped check-digit math.
What Is UPU S10?
The Universal Postal Union (UPU) is the United Nations agency that coordinates international mail. S10 is one of its technical standards, defining a 13-character identifier used on items sent across borders through the postal system.
The format is fixed:
Format: AA NNNNNNNN N AA
│ │ │ │
│ │ │ └─ 2-letter ISO country code of the origin
│ │ └─── Check digit (1 digit, computed from the serial)
│ └──────────── 8-digit serial number
└─────────────── 2-letter service indicator
Total length: 13 characters. 2 letters + 9 digits + 2 letters. That phrase shows up in dozens of search queries because it’s the single most useful way to remember the structure.
Breaking Down the 13 Characters
Characters 1–2 — Service Indicator (2 letters)
The first two letters tell you what kind of postal service the item used. The first letter is the service category, the second letter is a specific service variant within that category.
| First letter | Service category | Examples |
|---|---|---|
| E | Express Mail Service (EMS) | EE, EA, EM (priority international) |
| R | Registered Mail | RR, RA, RX (signed-for letters) |
| C | Insured Parcel | CC, CP, CX (tracked parcels) |
| L | Letter Post (signed) | LL, LX, LZ (small letter packets) |
| V | Insured Letter | VV, VR (declared-value letters) |
| U | Unregistered untracked | UA, UB (rare) |
Examples in the wild:
EE...— EMS ExpressRR...— Registered MailLX...— Tracked letter / small packetCP...— International Parcel
Characters 3–10 — 8-Digit Serial Number
Eight numeric digits, assigned sequentially by the originating postal operator. Nothing special about them on their own — but the next character depends on them.
Character 11 — Check Digit (1 digit)
This is the only character that matters for validation, and it’s the one most engineers miss. The check digit is computed from the 8 serial digits using a fixed weighted-sum formula.
Algorithm:
- Multiply each of the 8 serial digits by a fixed weight:
[8, 6, 4, 2, 3, 5, 9, 7] - Sum the products
- Compute
remainder = sum mod 11 - The check digit is:
0ifremainder == 15ifremainder == 0- otherwise
11 - remainder
JavaScript:
function s10CheckDigit(serial8) {
const weights = [8, 6, 4, 2, 3, 5, 9, 7];
const sum = serial8
.split('')
.reduce((acc, ch, i) => acc + Number(ch) * weights[i], 0);
const r = sum % 11;
if (r === 1) return 0;
if (r === 0) return 5;
return 11 - r;
}
function isValidS10(trackingNumber) {
if (!/^[A-Z]{2}\d{9}[A-Z]{2}$/.test(trackingNumber)) return false;
const serial = trackingNumber.slice(2, 10);
const claimed = Number(trackingNumber[10]);
return s10CheckDigit(serial) === claimed;
}
This catches the most common error — a single mistyped digit — without making any API call.
Characters 12–13 — Country Code (2 letters)
The final two characters are the ISO 3166-1 alpha-2 country code of the postal service that originated the item — not the destination.
| Code | Postal operator |
|---|---|
| US | USPS — United States Postal Service |
| GB | Royal Mail — United Kingdom |
| CN | China Post |
| JP | Japan Post |
| KR | Korea Post |
| DE | Deutsche Post |
| FR | La Poste |
| CA | Canada Post |
| AU | Australia Post |
| SG | Singapore Post |
| IN | India Post |
| NL | PostNL |
So LX123456787CN is a tracked letter shipped from China, regardless of where it’s headed.
Examples by Major Postal Carrier
| Tracking number | Service | Origin |
|---|---|---|
EE123456785US | USPS Priority Mail International / EMS | United States |
RR123456789GB | Royal Mail Tracked & Signed | United Kingdom |
LX123456784CN | China Post tracked small packet | China |
RR987654321JP | Japan Post Registered Mail | Japan |
EA123456789KR | Korea Post EMS | South Korea |
RA123456789DE | Deutsche Post Einschreiben (Registered) | Germany |
CP111222333CA | Canada Post International Parcel | Canada |
EM123456789AU | Australia Post Express Courier International | Australia |
(Check digits in this table are illustrative — your real numbers will compute differently.)
What S10 Does NOT Cover
S10 is for international postal items only. Domestic-only tracking numbers and private courier numbers use completely different formats:
- USPS domestic — 20–22 digits (
9400 1xxx...,9405 5xxx...) - UPS —
1Z+ 16 alphanumeric (1Z999AA10123456784) - FedEx — 12, 15, or 20 digits
- DHL Express — 10 digits, no letters
- CJ Logistics, Yamato, Sagawa — purely numeric, no S10 prefix
If a tracking number you’re handed doesn’t match ^[A-Z]{2}\d{9}[A-Z]{2}$, it isn’t S10. It might still be a perfectly valid tracking number — just from a different format family.
For a fuller breakdown of those domestic formats, see Understanding Tracking Number Formats by Carrier.
Common Misconceptions
- “The country code is the destination.” No — it’s the origin. A
...USnumber was sent from the United States, even if it ends up in Germany. - “The check digit is just a random digit.” It isn’t — it’s deterministic. If your input fails the check, the number is wrong.
- “All international tracking is S10.” No. Many private couriers (DHL Express, FedEx, UPS) carry international shipments under their own non-S10 numbering.
- “Lowercase is fine.” S10 numbers are always uppercase. Normalize the input before validating.
Validating S10 in Practice
Combine the regex shape check with the check-digit math. Here’s a complete validator that handles whitespace, case, and the math in one function:
function validateS10(input) {
const cleaned = input.replace(/[\s\-]/g, '').toUpperCase();
if (!/^[A-Z]{2}\d{9}[A-Z]{2}$/.test(cleaned)) {
return { valid: false, reason: 'wrong_shape' };
}
const serial = cleaned.slice(2, 10);
const claimed = Number(cleaned[10]);
const weights = [8, 6, 4, 2, 3, 5, 9, 7];
const sum = serial.split('').reduce((a, c, i) => a + Number(c) * weights[i], 0);
const r = sum % 11;
const expected = r === 1 ? 0 : r === 0 ? 5 : 11 - r;
if (expected !== claimed) {
return { valid: false, reason: 'check_digit_mismatch', expected };
}
return {
valid: true,
service: cleaned.slice(0, 2),
serial,
countryCode: cleaned.slice(11, 13),
};
}
This is good enough for client-side validation, customer-input forms, and pre-API filtering. It catches both “user typed it wrong” and “this isn’t actually a tracking number.”
Auto-Detection with WhereParcel
Even with perfect S10 validation, you still don’t know which postal operator to query — only the country of origin. You’d then need to map US → USPS, GB → Royal Mail, etc., and decide whether the destination’s postal service is also worth querying for inbound visibility.
WhereParcel handles this automatically. Pass carrier: "auto" and the API identifies the right operator, queries it, and returns a unified JSON status.
curl -X POST https://api.whereparcel.com/v2/track \
-H "Authorization: Bearer YOUR_API_KEY:YOUR_SECRET_KEY" \
-d '{"trackingItems": [{"carrier": "auto", "trackingNumber": "EE123456785US"}]}'
{
"success": true,
"data": {
"carrier": "us.usps",
"carrierName": "USPS",
"trackingNumber": "EE123456785US",
"status": "in_transit"
}
}
You write the validator once, the API takes over for the actual lookup.
Summary
The UPU S10 format is 2 letters + 9 digits + 2 letters:
- 2-letter service indicator (E/R/C/L/V/U + variant)
- 8-digit serial
- 1 check digit (deterministic, weighted-sum formula)
- 2-letter ISO country code (origin, not destination)
That’s it. Once you have the regex and the check-digit math, you can validate any S10 tracking number in any language in about 20 lines of code.
For the actual cross-carrier lookup, see the WhereParcel docs — auto-detection across 64+ live carriers is built in (request additions anytime).
Or paste any number into our free Tracking Number Validator — it auto-detects USPS, UPS, FedEx, DHL, OnTrac, Amazon Logistics, UPU S10, and more from the format alone (no signup needed).
Related guides: