⚡ Node.jsIoT / Hardware

IoT Device Identification — IMEI & MAC Address Validation

Validate IMEI numbers and MAC addresses in Node.js. Verify Luhn checksums, normalise MAC formats, detect multicast and locally-administered addresses, and run parallel checks with Promise.all.

Also available in Python

1. Device identifiers covered

IdentifierEndpointUse case
IMEI/v0/imei15-digit mobile device identifier — 3GPP standard
MAC Address/v0/mac-address48-bit hardware address — IEEE 802 standard

2. Validate IMEI

Endpoint: GET /v0/imei?value=…

{
  "valid": true,
  "imei": "352099001761481",
  "tac": "35209900",
  "fac": "00",
  "snr": "176148",
  "checkDigit": "1",
  "luhn": true
}
FieldCharactersMeaning
tac1–8Type Allocation Code — identifies manufacturer and model
fac9–10Final Assembly Code — manufacturing location
snr11–14Serial Number — unique per device
checkDigit15Luhn algorithm check digit
  1. Check valid and luhn
  2. Store tac to identify device model family
  3. Use snr as the unique unit identifier

3. Validate MAC address

Endpoint: GET /v0/mac-address?value=…

{
  "valid": true,
  "normalized": "00:1B:44:11:3A:B7",
  "format": "colon",
  "type": "unicast",
  "isMulticast": false,
  "isLocal": false,
  "isBroadcast": false
}
  1. Check valid
  2. Store normalized (colon-separated uppercase) as canonical form
  3. Reject isMulticast and isBroadcast for device registration
  4. Warn on isLocal — may be a randomised (privacy) MAC
⚠️Modern mobile OS versions (iOS 14+, Android 10+) randomise MAC addresses by default for Wi-Fi scanning. A locally-administered MAC (isLocal: true) may change across sessions and is not suitable as a persistent device identifier.

4. Parallel validation with Promise.all

import { createClient } from '@isvalid-dev/sdk'

const iv = createClient({ apiKey: process.env.ISVALID_API_KEY! })

interface DeviceRegistrationInput {
  imei?: string
  mac?: string
}

interface DeviceRegistrationResult {
  imei?: {
    valid: boolean
    imei: string
    tac: string
    fac: string
    snr: string
    checkDigit: string
    luhn: boolean
  }
  mac?: {
    valid: boolean
    normalized: string
    format: string
    type: string
    isMulticast: boolean
    isLocal: boolean
    isBroadcast: boolean
  }
  warnings: string[]
}

async function validateDevice(input: DeviceRegistrationInput): Promise<DeviceRegistrationResult> {
  const warnings: string[] = []
  const tasks: Promise<unknown>[] = []
  const keys: string[] = []

  if (input.imei) { tasks.push(iv.imei(input.imei)); keys.push('imei') }
  if (input.mac)  { tasks.push(iv.macAddress(input.mac)); keys.push('mac') }

  const settled = await Promise.allSettled(tasks)
  const results: Record<string, unknown> = {}

  keys.forEach((k, i) => {
    const r = settled[i]
    if (r.status === 'fulfilled') {
      results[k] = r.value
    } else {
      results[k] = { valid: false }
    }
  })

  const macResult = results.mac as { valid: boolean; isMulticast?: boolean; isLocal?: boolean; isBroadcast?: boolean } | undefined
  if (macResult?.valid) {
    if (macResult.isMulticast) warnings.push('MAC address is multicast — unlikely for a physical device')
    if (macResult.isLocal)     warnings.push('MAC address is locally administered — may be randomized')
    if (macResult.isBroadcast) warnings.push('MAC address is broadcast — not a valid device identifier')
  }

  return { ...results, warnings } as DeviceRegistrationResult
}

// Example
const result = await validateDevice({
  imei: '352099001761481',
  mac: '00:1B:44:11:3A:B7',
})
console.log('IMEI valid:', result.imei?.valid)
console.log('MAC normalized:', result.mac?.normalized)
console.log('Warnings:', result.warnings)

5. Edge cases

IMEI with dashes or spaces

💡Users often copy IMEIs from device settings in the format XX-XXXXXX-XXXXXX-X. The API accepts IMEIs with spaces, dashes, or as a plain 15-digit string.
// All of these are accepted:
await iv.imei('352099001761481')
await iv.imei('35-209900-176148-1')
await iv.imei('352 099 001 761 481')

MAC address formats

ℹ️The API accepts colon (00:1B:44:11:3A:B7), hyphen (00-1B-44-11-3A-B7), and compact (001B44113AB7) formats. Always use normalized for storage.

All-zeros or all-ones MAC

const mac = await iv.macAddress(userInput)
if (mac.valid && mac.isBroadcast) {
  // FF:FF:FF:FF:FF:FF is the broadcast address — reject for device registration
  throw new Error('Broadcast MAC address is not a valid device identifier')
}
// 00:00:00:00:00:00 will be invalid (not a real device)

6. Summary checklist

Validate IMEI Luhn checksum via API
Store TAC for device model family tracking
Store normalized MAC (colon uppercase)
Reject multicast and broadcast MACs
Warn on locally-administered (randomised) MACs
Run IMEI + MAC in parallel with Promise.all
Accept IMEI with dashes/spaces from device settings
Return 422 with field-level error messages

See also

Ready to integrate?

Free tier — 1,000 requests/month. No credit card required.

Get your API key →