This repository contains json files that programmatically describe how to detect, validate, and decode tracking numbers. The collection started in the tracking_number ruby gem in 2010, and split out here in 2017 so that any language could use it.
trackingnumber.fyi consumes the latest release of this data. Paste a number in and it will tell you which carrier it belongs to and illustrate, digit by digit, what each part of that number means.
| Language | Library | |
|---|---|---|
| Ruby | tracking_number | |
| JavaScript / TypeScript | ts-tracking-number | |
| Python | tracking-numbers | |
| Java | MysteryTrackingNumber | |
| Go | go-package-tracking | |
| Rust | rs-tracking-numbers |
Written one for a language that isn't listed? Open a PR to get it added to this list.
| Carrier | Formats |
|---|---|
| Amazon | Amazon Logistics · Amazon International |
| Canada Post | Canada Post (16) |
| Canpar | Canpar (22) |
| DHL | DHL Express · DHL Express (Piece ID) · DHL E-Commerce · DHL E-Commerce (14) |
| DPD | DPD (28) · DPD (14) |
| FedEx | FedEx Express (12) · FedEx Express (34) · FedEx ASTRA (32) · FedEx Ground · FedEx Ground (SSCC-18) · FedEx Ground 96 (22) · FedEx Ground GSN |
| GOFO Express | GOFO Express (US) |
| Landmark Global LTN | Landmark Global LTN |
| LaserShip | LaserShip LX · LaserShip 1LS7 (15) · LaserShip 1LS7 (18) · LaserShip 1LSCX (15) |
| Old Dominion Freight Line | Old Dominion · Old Dominion Guaranteed Shipment |
| OnTrac | OnTrac · OnTrac D |
| Purolator | Purolator (12) · Purolator (alpha + 9) |
| S10 International Standard | S10 |
| Spee-Dee Delivery | Spee-Dee (20) |
| United States Postal Service | USPS 20 · USPS IMpb N · USPS Legacy · USPS IMpb C |
| UPS | UPS · UPS Waybill |
| Yodel | Yodel |
| YunExpress | YunExpress |
Identifies the standard couriers that might send mail. Here is couriers/s10.json, shortened:
{
"name": "S10 International Standard",
"courier_code": "s10",
"tracking_numbers": [
{
"id": "s10",
"name": "S10",
"description": "The UPU format every postal service uses for international items",
"regex": "\\s*(?<ServiceType>([A-Z]\\s*){2})(?<SerialNumber>([0-9]\\s*){8})(?<CheckDigit>([0-9]\\s*))(?<CountryCode>([A-Z]\\s*){2})",
"validation": {
"checksum": { "name": "s10", "weightings": [8, 6, 4, 2, 3, 5, 9, 7], "modulo": 11 }
},
"glossary": {
"SerialNumber": { "description": "Eight digits assigned by the issuing postal service." }
},
"tracking_url": null,
"test_numbers": {
"valid": ["RB123456785GB", "RB123456785US"],
"invalid": ["RB123456786US", "RB123456785XX"]
}
}
]
}| Key | What it is |
|---|---|
name |
Identifies the courier |
courier_code |
Short code to identify the courier. Alphanumeric only, no spaces. |
tracking_numbers |
An array of possible tracking number formats for this courier |
| Key | What it is |
|---|---|
id |
Identifies this format no matter what it gets renamed to. Both partner_id and the format's address on the site point back at this |
name |
A name to identify this type of tracking number. Usually includes the carrier in the name, i.e. FedExGround |
regex |
A pcre compatible regular expression that identifies the tracking number regardless of spaces in-between characters. Either a string, or an array of strings to be concatenated (to help with readability). |
description |
(optional) A note about the format itself, such as "USPS now calls this the IMpb barcode format" |
glossary |
(optional) What this format's named groups mean when the general description isn't specific enough, keyed by group name |
validation |
Specifies how the tracking number is validated |
additional |
(optional) Further information relating to a named regex group, such as a lookup table for the ServiceType group |
partners |
(optional) A possible partnership between carriers, where one party is the shipper and the other the last mile carrier |
tracking_url |
A url that we can use to find the tracking history for a particular tracking number. It assumes the tracking number can be entered using python style string formatting "www.courier.com?trackingnumber=%s" |
test_numbers |
valid: an array of valid tracking numbers for testing, and invalid: an array of invalid tracking numbers for testing |
Every regex must contain the named groups SerialNumber and CheckDigit and depending on the tracking number can optionally contain the following common attributes:
ServiceType: indicating the type of delivery serviceShipperId: indicating the shipper idPackageId: indicating the package idDestinationZip: indicating the destination zip code
checksum: if the tracking number has a checksum, include a checksum key with the details. name specifies the algorithm. Supported algorithms are mod10, mod7, s10, luhn, mod_37_36 and sum_product_with_weightings_and_modulo.
"validation": {
"checksum": {
"name": "mod10",
"evens_multiplier": 1,
"odds_multiplier": 2
}
}Each checksum carries the constants its algorithm uses. Look at existing examples for parameters, or at CHECKSUM_ALGORITHMS.md for how each one works.
serial_number_format: some tracking numbers require some modification of the SerialNumber group before validation. In the example below, the serial number needs a "91" prepended before validation unless the number starts with a 91, 92, 93, 94, or 95
"serial_number_format": {
"prepend_if": {
"matches_regex": "^(?!9[1-5]).+",
"content": "91"
}
}additional: some tracking numbers are only valid if one of their additional lookups finds a match. S10 requires a Courier, so a country code no postal service uses is invalid even with the right check digit.
"validation": {
"additional": { "exists": ["Courier"] }
}A lookup table for the ServiceType regex group, relating the two digit letter code with the type of service:
"additional": [
{
"name": "Service Type",
"regex_group_name": "ServiceType",
"lookup": [
{ "matches": "01", "name": "UPS United States Next Day Air (Red)" },
{ "matches": "02", "name": "UPS United States Second Day Air (Blue)" }
]
}
]Each hash in the lookup array should contain a key called matches or matches_regex, specifying how the value of regex_group_name should be compared.
Each entry describes a possible partnership between carriers, where one party is the shipper and the other the last mile carrier. A partnership only holds if both ends pass their checks against the same number. Each item in the partners array should have:
partner_id: (required) reference indicating the related definitionpartner_type: (required) the relationship, eithershipperorcarrierdescription: (optional) mainly for humans reading thisvalidation: (optional) a validation block deciding whether this partnership applies, eithermatches_allormatches_any, an array of match conditions each with aregex_group_nameand either amatchesor amatches_regex
What each part means, keyed by the regex group name. Anything reading couriers/*.json can then describe a SerialNumber the same way everything else does.
"ServiceType": {
"label": "Service Type",
"description": "A code for the delivery service used."
}A definition's own glossary overrides the description when it can be more specific. For s10, the service type is two letters and the serial is eight digits assigned by the issuing postal service.
"glossary": {
"ServiceType": {
"description": "Two letters for the class of postal service, the first of which identifies the service and the second the variant."
},
"SerialNumber": {
"description": "Eight digits assigned by the issuing postal service."
}
}- Open an issue and specify the tracking numbers and courier service.
- Modify or add definitions in the couriers/*.json files. Take a look at the existing ones, and follow the guidance above. Use https://trackingnumber.fyi for guidance on check digit algorithms if needed.
- Run
./utils/lint_json.shto clean up and validate the json file (you may need jq or other dependencies). - Run the tests locally.
bundle exec rakeIf they pass, it's good, submit a PR!
The supported carriers table above is generated by CI and will update after a PR has been merged.
- Standard implementations of check digit algorithms and serial number parsing
- CHECKSUM_ALGORITHMS.md - how each checksum in this repo works
- Reference documents, located/uploaded to the wiki for preservation