Every Phone Has a Fingerprint
Every mobile device in the world has a unique 15-digit number burned into it: the IMEI (International Mobile Equipment Identity). If you've ever worked on mobile device management, telecom APIs, warranty systems, or repair tracking — you've dealt with IMEIs.
But most developers treat them as opaque strings. Let's crack them open.
IMEI Structure
An IMEI is exactly 15 digits, and every single one means something:
35 391104 227189 2
│ │ │ │
│ │ │ └─ Check digit (Luhn algorithm)
│ │ └─ Serial number (unique per device)
│ └─ Type Allocation Code (TAC) — identifies the model
└─ Reporting Body Identifier
The TAC (Type Allocation Code)
The first 8 digits identify the exact device model. This is assigned by the GSMA (the organization that manages mobile standards). If you see 35391104, that maps to a specific manufacturer and model.
This is incredibly useful:
- Device identification: Know the model from just the IMEI
- Fraud detection: Verify a claimed device model matches its IMEI
- Analytics: Track device types across a fleet without additional APIs
The Serial Number
Digits 9-14 are the serial number, unique within that TAC. This is how two identical iPhone models get different IMEIs.
The Check Digit
The 15th digit is calculated using the Luhn algorithm — the same checksum used in credit card numbers. This catches typos and transcription errors.
The Luhn Algorithm for IMEI
Here's how the check digit works:
function calculateLuhnCheckDigit(imei14) {
let sum = 0;
for (let i = 0; i < 14; i++) {
let digit = parseInt(imei14[i]);
// Double every other digit (from the right)
if (i % 2 !== 0) {
digit *= 2;
// If doubling results in > 9, subtract 9
if (digit > 9) digit -= 9;
}
sum += digit;
}
// Check digit makes the total sum divisible by 10
return (10 - (sum % 10)) % 10;
}
// Example:
calculateLuhnCheckDigit("35391104227189"); // → 2
// Full IMEI: 353911042271892
And to validate a complete 15-digit IMEI:
function isValidIMEI(imei) {
// Must be exactly 15 digits
if (!/^\d{15}$/.test(imei)) return false;
let sum = 0;
for (let i = 0; i < 15; i++) {
let digit = parseInt(imei[i]);
if (i % 2 !== 0) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
}
return sum % 10 === 0;
}
IMEI vs IMEI/SV vs MEID
You'll encounter variations:
| Identifier | Length | Network | Notes |
|---|---|---|---|
| IMEI | 15 digits | GSM/LTE/5G | Most common globally |
| IMEISV | 16 digits | Same | Adds software version, no check digit |
| MEID | 14 hex chars | CDMA | Used by older Verizon/Sprint devices |
For most modern development, you're dealing with standard 15-digit IMEIs.
Why Developers Need Test IMEIs
1. Mobile Device Management (MDM)
If you're building MDM solutions, you need to test device enrollment, policy assignment, and compliance checks. Real IMEIs are sensitive data — you can't scatter them through your test suite.
2. Telecom APIs
Network operators expose APIs for device registration, stolen device checks (GSMA blacklist), and warranty lookups. Your integration tests need valid-format IMEIs.
3. Repair & Warranty Systems
Apple, Samsung, and other OEMs use IMEI for warranty validation. If you're building a repair tracking system, your test data needs to pass format validation.
4. E-commerce & Second-hand Markets
Platforms like Swappa, Back Market, and others validate IMEIs to check for stolen/blacklisted devices. Testing these flows requires properly formatted IMEIs.
Generating Test IMEIs
I built Random IMEI Generator for exactly this use case. It generates structurally valid IMEIs with correct Luhn check digits.
What you get:
- Valid 15-digit IMEIs with correct check digits
- Proper TAC structure
- Bulk generation for test suites
- Client-side only — no data leaves your browser
Important: Generated IMEIs are for testing and development only. They pass format validation but are not registered with any carrier or associated with real devices.
Building IMEI Validation Into Your App
Here's a production-ready validator with detailed error reporting:
function validateIMEI(imei) {
const errors = [];
const clean = imei.replace(/[\s-]/g, '\);
// Length check
if (clean.length !== 15) {
errors.push(`Expected 15 digits, got ${clean.length}`);
}
// Digits only
if (!/^\d+$/.test(clean)) {
errors.push('IMEI must contain only digits');
}
// TAC check (first 8 digits shouldn't be all zeros)
if (/^0{8}/.test(clean)) {
errors.push('Invalid TAC (Type Allocation Code)');
}
// Luhn check
if (clean.length === 15 && /^\d+$/.test(clean)) {
let sum = 0;
for (let i = 0; i < 15; i++) {
let d = parseInt(clean[i]);
if (i % 2 !== 0) {
d *= 2;
if (d > 9) d -= 9;
}
sum += d;
}
if (sum % 10 !== 0) {
errors.push('Luhn check digit is invalid');
}
}
return {
valid: errors.length === 0,
errors,
tac: clean.slice(0, 8),
serial: clean.slice(8, 14),
checkDigit: clean[14]
};
}
// Usage:
validateIMEI('353911042271892');
// {
// valid: true,
// errors: [],
// tac: '35391104',
// serial: '227189',
// checkDigit: '2'
// }
Fun Facts About IMEIs
- There are over 5 billion active IMEIs in the world
- Dual-SIM phones have two IMEIs (one per SIM slot)
- The GSMA maintains a central database called the EIR (Equipment Identity Register) that tracks stolen devices
- Changing an IMEI is illegal in many countries (UK, India, UAE, and others)
- Some eSIM devices use EIDs (32-digit identifiers) alongside their IMEI
IMEI in Different Contexts
// Getting IMEI on Android (requires READ_PHONE_STATE permission)
// TelephonyManager.getImei()
// iOS doesn't expose IMEI to apps (privacy restriction since iOS 5)
// Use identifierForVendor instead
// In web apps, you can't access IMEI directly
// Accept it as user input and validate the format
TL;DR
- IMEI = 15-digit unique device identifier (8-digit TAC + 6-digit serial + 1 check digit)
- Check digit uses the Luhn algorithm (same as credit cards)
- The TAC identifies the exact device model
- Use randomimei.com for generating test IMEIs with valid checksums
- Never use real IMEIs in test code — they're sensitive identifiers
Part of the Developer Tools Deep Dives series. Building free, no-signup developer tools at namso.io and related sites.
Top comments (0)