MRZ Formats: TD1, TD2 and TD3 (Differences and Detection)
The three ICAO 9303 machine readable zone formats: how many lines each has, which documents use them, where the field positions differ, and how to detect the format automatically.
Not all MRZs are the same. ICAO 9303 defines three formats according to the document's physical size, and confusing them causes an entire class of integration bug: the code reads TD3 positions off a TD1 and returns garbage without complaining.
The three formats
| Format | Lines | Characters per line | Total | Typical documents |
|---|---|---|---|---|
| TD1 | 3 | 30 | 90 | ID cards, residence permits |
| TD2 | 2 | 36 | 72 | Smaller booklet travel documents |
| TD3 | 2 | 44 | 88 | Passports |
If you work with passports, you work with TD3 nearly always. The other two show up when your workflow also accepts identity cards or residence permits.
TD3 — the passport
Two lines of 44 characters. This is the format we've been working through:
P<MEXGOMEZ<VELAZQUEZ<<MARGARITA<<<<<<<<<<<<<
G123456786MEX8007050F3307054<<<<<<<<<<<<<<08
Line 1 carries type, issuing country and name; line 2 carries all the verifiable data. The complete position map is in How to read the MRZ line by line.
TD1 — the card
Three lines of 30 characters. This is the credential-sized format, and its layout is markedly different:
I<UTOD231458907<<<<<<<<<<<<<<<
7408122F1204159UTO<<<<<<<<<<<6
ERIKSSON<<ANNA<MARIA<<<<<<<<<<
The differences that break TD3-shaped code:
- The name is on line 3, not line 1.
- The document number is on line 1 (positions 6-14), not line 2.
- The dates are on line 2: birth at 1-6, expiry at 9-14.
- There's a composite check digit at the end of line 2 spanning fragments of lines 1 and 2.
A reader that assumes "the name is on the first line" will return the document number as a surname. And it'll do it silently, because syntactically nothing is invalid.
TD2 — the middle case
Two lines of 36 characters. Structurally it resembles TD3 — name on line 1, data on line 2 — but every line 2 position is shifted, because the name and personal number fields are shorter.
This is the subtlest trap of the three: TD3-shaped code doesn't fail loudly on a TD2, it reads fields offset by a few positions. The result has the right shape and the wrong contents.
Detecting the format automatically
Detection is trivial and there's no excuse for skipping it: the format is determined by the number of lines and their length.
function detectMrzFormat(lines) {
const clean = lines
.map((l) => l.trim().toUpperCase())
.filter(Boolean)
const n = clean.length
const lengths = clean.map((l) => l.length)
const allAre = (len) => lengths.every((l) => l === len)
if (n === 3 && allAre(30)) return 'TD1'
if (n === 2 && allAre(36)) return 'TD2'
if (n === 2 && allAre(44)) return 'TD3'
return null // Unknown format: don't guess at positions.
}
That return null is the important part. When the format isn't recognised, the correct outcome is to reject, not to pick the most likely one. Guessing produces plausible, false data — the worst category of failure, because it passes your system's shape validation and lands in the database.
What all three share
Underneath the layout differences, the mechanics are identical across formats:
- Alphabet: only
A-Z,0-9and<. - Padding:
<fills every field to its fixed length. - Names: surnames,
<<, given names,<between words. - Dates:
YYMMDD, no century. - Check digits: the same 7-3-1 modulo 10 algorithm, explained in ICAO 9303 check digits.
In other words: if you've implemented check digit computation for TD3, it works unchanged for the other two. All that changes is which spans the data comes from.
When you'll meet each one
In a real identity verification flow:
- Passport (TD3) — travellers, foreign nationals, international KYC. The most common by far.
- Identity card (TD1) — when you accept national IDs from countries that issue card-format documents.
- TD2 — least frequent; appears with certain travel documents and permits.
If your product accepts "government-issued ID" without narrowing the type, all three will arrive eventually. Better to detect the format from day one than to discover it when a customer reports shifted names.
Try it
Our MRZ validator is built for TD3, the passport format: paste both lines and you'll see each field decoded with all four check digits recomputed. It's the same code that runs in our extraction engine, so what you see there is what we apply in production.
Our API likewise specialises in passports, returning both MRZ lines complete and validated alongside the structured fields. You can try it without signing up in the free demo.
Need to extract passport data automatically?
Try our API with 20 free extractions. Integrate in minutes, get results in seconds.
Start for free