# Container Number Verification: The ISO 6346 Check Digit

> The full ISO 6346 check digit algorithm for container number verification, a worked example, and why it catches OCR misreads before they become tracking errors.

**Canonical URL:** https://docsapi.co/resources/blogs/container-number-verification
**Author:** Nupura Ughade — Content Marketing Lead, DocsAPI
**Author LinkedIn:** https://www.linkedin.com/in/nupura-ughade/
**Published:** 2026-09-06T00:00:00.000Z
**Updated:** September 6, 2026
**Primary topic:** container number verification
**Site:** https://docsapi.co (DocsAPI — Document AI & OCR API for SMB Lending)

---

Two container numbers, one digit apart: HLXU 659347 and HLXU 659847. A human glancing at a scanned bill of lading would probably not notice the swap, a 3 that got read as an 8 in a slightly smudged stencil photo. But run either string through the ISO 6346 check digit calculation and the difference stops being subtle. The real container's check digit is 7. The misread version demands a check digit of 9. Nothing about the two strings looks obviously wrong to a person, and everything about them is distinguishable to an algorithm that takes eleven characters seriously. That gap, between what looks fine and what actually validates, is the entire reason container number verification exists as a discrete step in a shipping document pipeline rather than something you assume OCR gets right because the text extraction confidence score said 98 percent.

This post walks through the actual published algorithm behind that check digit, computes it by hand on a real container number, then breaks it deliberately to show what a single misread digit does to the math. If you are building or evaluating a system that extracts container numbers from bills of lading, container gate receipts, or terminal EDI messages as part of a broader [shipping document processing](/documents/shipping-docs) pipeline, this is the layer that turns "we extracted some text" into "we extracted a container number we can trust downstream."

## What an ISO 6346 container number actually contains

Every standard shipping container carries an eleven character identifier defined by ISO 6346, the international standard governing freight container coding, identification, and marking. The identifier is not a random serial number. It is built from four distinct fields, each with a fixed length and a fixed job.

| Field | Length | Content | Example (CSQU3054383) |
| --- | --- | --- | --- |
| Owner code | 3 letters | Registered with the Bureau International des Containers, uniquely identifies the leasing or operating company | CSQ |
| Category identifier | 1 letter | Always U for freight containers, J for detachable freight container-related equipment, or Z for trailers and chassis | U |
| Serial number | 6 digits | Assigned by the owner, unique within that owner's fleet | 305438 |
| Check digit | 1 digit | Computed from the ten characters before it, used to verify the whole string was read or transcribed correctly | 3 |

The owner code plus category identifier form what the industry calls the BIC code, and the registry behind it is what makes a container number globally unique rather than just unique within one shipping line's own numbering scheme. But the field that matters for this post is the last one. The check digit is not part of the container's identity in any meaningful sense, an owner could not choose it and it carries no information about the box itself. It exists purely as a mathematical fingerprint of the ten characters that came before it, and that is precisely what makes it useful for catching errors that a spell checker, a format regex, or a human proofreader would miss.

## The letter to number conversion table

The check digit calculation needs every character in the container number to be a number, but the first four characters are letters. ISO 6346 solves this with a fixed conversion table that assigns each letter of the alphabet a numeric equivalent, starting at 10 for A and counting upward, but deliberately skipping every multiple of 11 along the way.

| Letter | Value | Letter | Value | Letter | Value |
| --- | --- | --- | --- | --- | --- |
| A | 10 | J | 20 | S | 30 |
| B | 12 | K | 21 | T | 31 |
| C | 13 | L | 23 | U | 32 |
| D | 14 | M | 24 | V | 34 |
| E | 15 | N | 25 | W | 35 |
| F | 16 | O | 26 | X | 36 |
| G | 17 | P | 27 | Y | 37 |
| H | 18 | Q | 28 | Z | 38 |
| I | 19 | R | 29 |  |  |

Notice what is missing from that sequence: 11, 22, and 33 never appear as a letter value. That is not an oversight, it is deliberate. The final step of the algorithm divides by 11, and if a letter value were itself a multiple of 11, it could interact with that division in a way that produces ambiguous or degenerate results for certain character combinations. Skipping those three values keeps every letter's contribution to the final sum mathematically well behaved under a modulo 11 check. It is a small design choice, but it is the kind of detail that separates a standard someone actually stress tested against edge cases from one that just seemed to work on the examples someone happened to try.

## The weighted sum algorithm, step by step

With every character converted to a number, ISO 6346 assigns each of the ten positions, four letters and six digits, a weight equal to 2 raised to that position's index, counting left to right starting at zero.

| Position | 1st | 2nd | 3rd | 4th | 5th | 6th | 7th | 8th | 9th | 10th |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Exponent | 2^0 | 2^1 | 2^2 | 2^3 | 2^4 | 2^5 | 2^6 | 2^7 | 2^8 | 2^9 |
| Weight | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 |

The full procedure is four steps. First, convert each of the four owner and category letters to its numeric equivalent using the table above, and take each of the six serial number digits at face value. Second, multiply each of those ten values by the weight assigned to its position, so the first character is multiplied by 1, the second by 2, the third by 4, and so on, doubling each time up through the tenth character multiplied by 512. Third, add all ten products together into a single sum. Fourth, divide that sum by 11 and keep the remainder. That remainder is the check digit, with one exception: if the remainder comes out to exactly 10, the check digit is written as 0 instead, because a single digit position cannot hold a two digit value.

## A full worked example: CSQU 305438

Take the container number CSQU3054383, a commonly cited real world example of a correctly formatted ISO 6346 identifier. Strip off the check digit and work through the first ten characters, CSQU305438, one position at a time.

| Character | C | S | Q | U | 3 | 0 | 5 | 4 | 3 | 8 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Numeric value | 13 | 30 | 28 | 32 | 3 | 0 | 5 | 4 | 3 | 8 |
| Weight | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 |
| Product | 13 | 60 | 112 | 256 | 48 | 0 | 320 | 512 | 768 | 4096 |

Add the ten products: 13 + 60 + 112 + 256 + 48 + 0 + 320 + 512 + 768 + 4096 equals 6185. Divide 6185 by 11: 11 goes into 6185 exactly 562 times with a remainder of 3, since 562 times 11 is 6182 and 6185 minus 6182 is 3. The remainder is 3, it is not 10, so no substitution is needed. The check digit is 3, which matches the published check digit on CSQU3054383 exactly. That is the whole algorithm, run on a real number, arriving at the real answer.

## A second example, run in the verification direction

The first example computed a check digit from scratch. In an actual document pipeline, verification usually runs the other direction: you already have all eleven characters from an OCR pass or an EDI message, and you need to confirm the eleventh matches what the first ten predict. Take a constructed but algorithmically genuine example, MSCU6652830.

Converting and weighting MSCU665283 the same way: M is 24, S is 30, C is 13, U is 32, then the digits 6, 6, 5, 2, 8, 3 stay as written. Multiplying by weights 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 in order gives products of 24, 60, 52, 256, 96, 192, 320, 256, 2048, and 1536. Summed, those come to 4840. Divide 4840 by 11: 11 times 440 is 4840 exactly, a remainder of 0. The check digit is 0, so MSCU6652830 is internally consistent, an OCR engine or EDI parser that extracted exactly those eleven characters can trust the extraction was clean. If the extracted string had instead read MSCU6652831 or any other trailing digit besides 0, the verification step would flag it immediately, before that container number ever reached a tracking system, a customs filing, or a terminal appointment booking.

## Why this catches OCR misreads before they become tracking errors

Go back to the pair from the opening: HLXU659347 as the correct container number, and HLXU659847 as what an OCR engine might output if the printed 3 in the eighth position has degraded stenciling, glare, or a paint chip that makes it resemble an 8. Running the algorithm on the correct string, HLXU659347, converts H to 18, L to 23, X to 36, U to 32, then the digits 6, 5, 9, 3, 4, 7. Weighted and summed, that comes to 6288, and 6288 divided by 11 is 571 with a remainder of 7. The correct check digit is 7. Running the same algorithm on the misread string, HLXU659847, changes only the eighth character's contribution, from 3 times weight 128 (384) to 8 times weight 128 (1024), a difference of 640. The new sum is 6928, and 6928 divided by 11 is 629 with a remainder of 9. The misread version demands a check digit of 9.

If the container's actual stenciled check digit is 7, and the OCR engine also correctly read that final digit as 7 while misreading the eighth character as an 8, the mismatch between the computed check digit (9, based on the misread serial) and the extracted check digit (7, correctly read) is immediate and unambiguous. The system does not need a second photo, a manual review queue, or a human glancing at the image to catch that something is wrong. The arithmetic itself flags it, in the time it takes to run ten multiplications and a division.

| Scenario | Extracted serial digits | Extracted check digit | Computed check digit | Result |
| --- | --- | --- | --- | --- |
| Clean read | 659347 | 7 | 7 | Match, accept |
| OCR misread 3 as 8 | 659847 | 7 | 9 | Mismatch, reject and re-verify |
| OCR misread 0 as 8 (owner code letter unaffected, digit only) | 659340 read as 659348 | varies | varies | Mismatch, reject and re-verify |

This matters more in shipping documents than in most other OCR use cases because a container number does not travel alone. The same eleven characters get keyed, scanned, or transmitted at the port of loading, at the ocean carrier's booking system, in an EDI 315 or CODECO status message, at the customs entry filing, and again at the destination terminal's gate system. An error introduced at any one of those points and not caught propagates into every downstream system that trusts the record it inherited. A container that gets mis-tracked because one system has HLXU659347 and another has HLXU659847 is not a minor inconvenience, it can mean a demurrage clock running against the wrong box, a customs hold triggered on a number that does not match the manifest, or a warehouse team searching a yard for a container that technically does not exist under the number they were given.

## What a checksum field changes about an OCR pipeline

Most fields on a shipping document have no built in error detection. A consignee name, a port of discharge, a commodity description, all of these can be misread by OCR and the only way to catch the error is a downstream mismatch against another document, a human review, or the error simply persisting silently. A container number is structurally different, and a document intelligence pipeline should treat it differently.

| Field type | Self-validating | How an error surfaces | Detection latency |
| --- | --- | --- | --- |
| Container number (ISO 6346) | Yes, via check digit | Computed check digit disagrees with extracted check digit | Immediate, at extraction time |
| Bill of lading number | No standard checksum | Cross-document mismatch or downstream rejection | Hours to days later |
| HS code | Partially, via chapter/heading structure but no arithmetic check digit | Customs system rejection | At filing, often after other processing has occurred |
| Consignee name or address | No | Delivery failure or manual review | Delivery attempt, days later |

That difference is the whole argument for running the check digit calculation as a mandatory validation step immediately after OCR extraction, not as an optional data quality check run later in a batch job. A container number is one of the few fields on a shipping document where the document format itself hands you a free error detector, and skipping it means throwing away verification that costs a handful of arithmetic operations and catching nothing that the check digit would have caught for free.

## What check digit verification does not catch

It is worth being precise about the limits here, because a check digit is not a spell checker for the entire container number. The algorithm verifies internal mathematical consistency among the eleven characters, nothing more. If an OCR engine misreads two characters in a way that happens to produce the same check digit as the correct string, and that can happen, a check digit collision is mathematically possible, the error passes through undetected. Similarly, if a container's stenciled digits are correctly read but the container itself is the wrong one entirely, a legitimate, internally consistent number belonging to a different box, the check digit offers no help, because ISO 6346 has no way to know which specific physical container a given valid number should be attached to. Check digit verification is a necessary filter against transcription and OCR errors within a single field. It is not a substitute for cross-referencing the container number against a booking confirmation, a bill of lading, or a gate transaction to confirm the right container is actually the one in question.

## Building this into a document pipeline

In practice, container number verification belongs as one narrow, deterministic rule sitting right after OCR extraction and right before that extracted value gets written anywhere else, a database, an EDI outbound message, a customs filing draft. The logic itself is small, a lookup table, a weighted sum, a modulo operation, but the discipline of always running it, on every container number pulled from a [bill of lading](/resources/blogs/bill-of-lading-processing), a container gate receipt, or a [shipment status EDI message](/resources/blogs/shipment-status-edi-processing), is what turns it from a nice-to-have into a real quality gate. The same logic applies wherever container numbers get re-keyed or cross-checked, including alongside a [VGM declaration](/resources/blogs/vgm-declaration-processing), where the container number has to match exactly across the weighing record and the shipping instruction for the declaration to be valid at all.

What makes this worth automating rather than leaving to a reviewer's eye is exactly what the HLXU example demonstrated: a one digit OCR error does not look wrong to a person skimming a scanned document, and it should not have to. The check digit exists so that a system does not need a human to notice a subtle glyph confusion. It needs ten multiplications, an addition, and a division, run every time, on every extracted container number, before that number gets trusted anywhere downstream.

Written by [Nupura Ughade](/author/nupura-ughade).

## Frequently Asked Questions

### What is the ISO 6346 check digit algorithm?

It converts the four owner and category letters of a container number to numeric values using a fixed table, converts the six serial digits directly, multiplies each of the ten resulting values by a weight of 2 raised to its position (1, 2, 4, 8, 16, 32, 64, 128, 256, 512), sums the products, and divides by 11. The remainder is the check digit, except a remainder of 10 becomes 0.

### Why do the letter values skip 11, 22, and 33?

ISO 6346's conversion table assigns letters values starting at 10 for A and counting up, but deliberately omits every multiple of 11 so that no letter value interacts ambiguously with the modulo 11 division used in the final check digit calculation.

### What is the check digit for CSQU3054383?

Converting and weighting the first ten characters, CSQU305438, produces a sum of 6185. Dividing by 11 gives a remainder of 3, which is the correct check digit, matching the published number CSQU3054383.

### Can a check digit catch every OCR error in a container number?

No. It reliably catches single-character misreads and most multi-character misreads because they almost always change the computed remainder. It cannot catch the rare case where two errors happen to cancel out and still produce the correct check digit, and it cannot detect that a correctly read number belongs to the wrong physical container.

### What does the fourth letter in a container number mean?

The first three letters are the owner code, registered through the BIC registry. The fourth letter is the category identifier, which is U for standard freight containers, J for detachable freight container-related equipment, or Z for trailers and chassis.

### Why does container number verification matter more than validating other shipping document fields?

Most shipping document fields, like a consignee name or a bill of lading number, have no built-in error detection, so an OCR mistake only surfaces later through a mismatch or a failed delivery. A container number carries its own check digit, so an extraction error can be caught immediately, before it propagates into booking systems, EDI messages, or customs filings.


---

**Source URL (cite this):** https://docsapi.co/resources/blogs/container-number-verification
**Author profile:** https://docsapi.co/author/nupura-ughade
**Published by:** DocsAPI (https://docsapi.co)
