Container Code Validation in Python — ISO 6346 Explained
Every shipping container crossing the ocean carries a unique 11-character code governed by ISO 6346. Here's how the standard works, what each part of the code means, how the mod-11 check digit catches transposition errors, and how to validate container codes in your Python application.
In this guide
1. What is ISO 6346?
ISO 6346 is an international standard that defines the coding, identification, and marking of shipping containers used in intermodal freight transport. Published by the International Organization for Standardization, it assigns each container a globally unique 11-character code consisting of letters and digits.
The standard is maintained by the Bureau International des Containers (BIC), which operates the global registry of owner codes. Every container manufacturer, leasing company, and shipping line must register its owner code with BIC before putting containers into service.
In logistics software, container codes appear in bills of lading, customs declarations, terminal operating systems, and vessel manifests. A single transposition error can route a container to the wrong yard, delay customs clearance, or generate phantom inventory.
2. Anatomy of a container code
An ISO 6346 container code has exactly 11 characters, split into four parts:
Owner Code
CSQ
3 letters — BIC-registered
Category
U
1 letter — equipment type
Serial Number
305438
6 digits — unique per owner
Check Digit
3
1 digit — mod-11 validation
3. The mod-11 check digit algorithm
ISO 6346 uses a weighted mod-11 algorithm. Each of the first 10 characters is assigned a numeric value, multiplied by a power-of-2 weight, and the sum modulo 11 produces the check digit. If the result is 10, it is mapped to 0.
Let's walk through CSQU3054383:
Step 1 — Convert letters to numbers
Letters are mapped: A=10, B=12, C=13, D=14, ... skipping multiples of 11 (11, 22, 33). So A=10, B=12, C=13, ..., K=21, L=23, ..., and so on. Digits 0-9 keep their face value.
| Character | C | S | Q | U | 3 | 0 | 5 | 4 | 3 | 8 | 3 |
| Value | 13 | 30 | 28 | 32 | 3 | 0 | 5 | 4 | 3 | 8 | ? |
Step 2 — Multiply by powers of 2
| Weight (2^i) | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | — |
| Product | 13 | 60 | 112 | 256 | 48 | 0 | 320 | 512 | 768 | 4096 | ? |
Step 3 — Sum and apply mod 11
13 + 60 + 112 + 256 + 48 + 0 + 320 + 512 + 768 + 4096 = 6185
6185 % 11 = 3
Check digit is 3 — matches the 11th character of CSQU3054383
# container_check_digit.py — ISO 6346 mod-11 check digit import re def validate_container_code(code: str) -> bool: code = code.upper().replace(" ", "").replace("-", "") if not re.match(r"^[A-Z]{3}[UJZR]\d{7}$", code): return False def char_value(ch: str) -> int: if ch.isdigit(): return int(ch) n = ord(ch) - ord("A") # value = 10 + n + number of multiples of 11 up to (10 + n) return 10 + n + (10 + n) // 11 total = sum( char_value(code[i]) * (2 ** i) for i in range(10) ) check_digit = total % 11 % 10 # if mod-11 gives 10, map to 0 return check_digit == int(code[10]) print(validate_container_code("CSQU3054383")) # True ✓ print(validate_container_code("CSQU3054384")) # False ✗ wrong check digit print(validate_container_code("MAEU1234567")) # checks dynamically
4. Category codes — U, J, Z, R
The fourth character of a container code identifies the type of equipment. ISO 6346 defines four category identifiers:
| Code | Category | Description |
|---|---|---|
| U | Freight container | Standard dry, high-cube, open-top, flat-rack, and other general-purpose containers |
| J | Detachable equipment | Detachable freight container-related equipment such as generator sets |
| Z | Trailer / Chassis | Trailers and chassis used in intermodal transport |
| R | Reefer container | Refrigerated containers with integrated cooling units |
U (freight container). Category R for reefers is less common in ISO 6346 markings because many reefer containers are still marked with U and identified as reefers through their ISO size-type code instead.5. Why container code validation matters
Customs and regulatory compliance
Customs authorities worldwide require valid container codes on all import/export declarations. An invalid code will cause the declaration to be rejected, delaying cargo clearance and potentially incurring demurrage charges.
Terminal operating systems
Port terminals use container codes to track containers through gate-in, yard placement, vessel loading, and gate-out. A mistyped code creates phantom entries in the yard plan — operators search for a container that appears to exist but cannot be physically located.
BIC registry and owner identification
The three-letter owner code is registered with BIC (Bureau International des Containers). Validating the check digit confirms the code was transcribed correctly, but does not confirm the owner code is registered. For owner verification, a separate BIC registry lookup is needed.
Supply chain visibility
Freight forwarders, 3PLs, and BCOs track containers across multiple carriers and modes. A single digit error in a container code breaks the tracking chain and makes the shipment invisible until someone manually reconciles the records.
6. The production-ready solution
The IsValid Container Code API validates the format, computes the ISO 6346 mod-11 check digit, and returns the parsed components — owner code, category, serial number, and check digit — in a single call.
Full parameter reference and response schema: Container Code Validation API docs →
7. Python code example
Using the isvalid-sdk package or the requests library. Install with pip install isvalid-sdk or pip install requests.
# container_validator.py import os from isvalid_sdk import IsValidConfig, create_client iv = create_client(IsValidConfig(api_key=os.environ["ISVALID_API_KEY"])) result = iv.container_code("CSQU3054383") if not result["valid"]: print("Invalid container code") else: print(f"Owner code: {result['ownerCode']}") # → 'CSQ' print(f"Category: {result['categoryName']}") # → 'Freight container' print(f"Serial: {result['serialNumber']}") # → '305438' print(f"Check digit: {result['checkDigit']}") # → '3'
In a cargo manifest processing pipeline:
# Validate container codes in a vessel manifest def process_manifest(containers: list[dict]) -> list[dict]: results = [] for entry in containers: if not entry.get("container_code"): results.append({**entry, "status": "missing"}) continue check = validate_container_code(entry["container_code"]) if not check["valid"]: results.append({**entry, "status": "invalid"}) continue results.append({ **entry, "container_code": entry["container_code"].upper().replace(" ", "").replace("-", ""), "owner_code": check["ownerCode"], "category": check["categoryName"], "serial_number": check["serialNumber"], "status": "valid", }) return results
CSQU 305438-3.8. cURL example
Validate a standard freight container:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/container-code?value=CSQU3054383"
Validate a container with an invalid check digit:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/container-code?value=CSQU3054380"
Validate a reefer container:
curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://api.isvalid.dev/v0/container-code?value=TRIU4567890"
9. Understanding the response
Valid container code:
{ "valid": true, "ownerCode": "CSQ", "categoryCode": "U", "categoryName": "Freight container", "serialNumber": "305438", "checkDigit": "3" }
Invalid container code:
{ "valid": false }
| Field | Type | Description |
|---|---|---|
| valid | boolean | Format correct, category valid, mod-11 check digit passes |
| ownerCode | string | 3-letter BIC owner code (e.g. "CSQ", "MAE", "MSC") |
| categoryCode | string | Single letter: U, J, Z, or R |
| categoryName | string | Human-readable category name (e.g. "Freight container") |
| serialNumber | string | 6-digit serial number assigned by the owner |
| checkDigit | string | Single check digit (0-9) computed via mod-11 |
10. Edge cases — tanks, reefers, and more
Tank containers
ISO tank containers (for liquids, gases, and powders) use the same coding scheme with category U. They are distinguished from dry containers by the ISO size-type code painted on the container (e.g. 22T0 for a standard tank), not by the owner/category code.
Reefer containers and the R category
While ISO 6346 defines R for reefer containers, in practice most reefer containers carry a U category code. The reefer nature is identified by the size-type code (e.g. 45R1). Your validation should accept both U and R for reefer containers.
Check digit 10 → 0 mapping
When the mod-11 computation yields 10, the check digit is mapped to 0. This is a known edge case — approximately 1 in 11 containers will have this mapping. If your validation logic does not handle this, it will incorrectly reject valid containers.
# The critical line in the check digit calculation: check_digit = total % 11 % 10 # double-mod handles 10 → 0
Lowercase and formatting variants
Container codes may arrive from OCR systems, manual data entry, or EDI messages in mixed case or with embedded spaces/hyphens. Always normalise to uppercase and strip non-alphanumeric characters before validation. The code csqu 305438-3 is the same container as CSQU3054383.
Summary
See also
Validate container codes instantly
Free tier includes 100 API calls per day. No credit card required. Full ISO 6346 validation with parsed owner code, category, and check digit.