🐍 PythonIoT / Hardware
IoT Device Identification — IMEI & MAC Address Validation
Validate IMEI numbers and MAC addresses in Python. Verify Luhn checksums, normalise MAC formats, detect multicast and locally-administered addresses, and run parallel checks with asyncio.gather.
Also available in Node.js
Contents
1. Validate IMEI
Use await iv.imei(value) (SDK) or GET /v0/imei?value=….
{ "valid": true, "imei": "352099001761481", "tac": "35209900", "fac": "00", "snr": "176148", "checkDigit": "1", "luhn": true }
- Check
validandluhn - Store
tacfor device model family tracking - Use
snras unique unit identifier
2. Validate MAC address
Use await iv.mac_address(value) (SDK) or GET /v0/mac-address?value=….
{ "valid": true, "normalized": "00:1B:44:11:3A:B7", "format": "colon", "type": "unicast", "isMulticast": false, "isLocal": false, "isBroadcast": false }
- Check
valid - Store
normalizedas canonical form - Reject
is_multicastandis_broadcast - Warn on
is_local— may be randomised
⚠️iOS 14+ and Android 10+ randomise MAC addresses for Wi-Fi scanning by default. A locally-administered MAC (
is_local = True) may change across sessions and is unsuitable as a persistent device identifier.3. Parallel validation with asyncio.gather
import asyncio from isvalid_sdk import IsValidConfig, create_client config = IsValidConfig(api_key="YOUR_API_KEY") iv = create_client(config) async def validate_device(imei: str | None = None, mac: str | None = None): tasks = {} if imei: tasks["imei"] = iv.imei(imei) if mac: tasks["mac"] = iv.mac_address(mac) results = dict(zip(tasks.keys(), await asyncio.gather(*tasks.values()))) warnings = [] mac_r = results.get("mac") if mac_r and mac_r.valid: if mac_r.is_multicast: warnings.append("MAC is multicast — unlikely for a physical device") if mac_r.is_local: warnings.append("MAC is locally administered — may be randomised (iOS/Android privacy MAC)") if mac_r.is_broadcast: raise ValueError("Broadcast MAC is not a valid device identifier") return {"results": results, "warnings": warnings} async def main(): device = await validate_device( imei="352099001761481", mac="00:1B:44:11:3A:B7", ) imei_r = device["results"]["imei"] mac_r = device["results"]["mac"] print(f"IMEI valid: {imei_r.valid}, TAC: {imei_r.tac}") print(f"MAC valid: {mac_r.valid}, normalized: {mac_r.normalized}") if device["warnings"]: print(f"Warnings: {device['warnings']}") asyncio.run(main())
4. Edge cases
IMEI with dashes or spaces
💡The API accepts IMEIs with dashes, spaces, or as a plain 15-digit string — all common from device settings.
# All accepted: await iv.imei("352099001761481") await iv.imei("35-209900-176148-1") await iv.imei("352 099 001 761 481")
MAC format normalisation
# All accepted — normalized form is always returned result = await iv.mac_address("001B44113AB7") # compact result = await iv.mac_address("00-1B-44-11-3A-B7") # hyphen result = await iv.mac_address("00:1b:44:11:3a:b7") # lowercase print(result.normalized) # "00:1B:44:11:3A:B7" — always uppercase colon form
Batch device validation
device_pairs = [ {"imei": "352099001761481", "mac": "00:1B:44:11:3A:B7"}, {"imei": "490154203237518", "mac": "AC:BC:32:A1:B2:C3"}, ] async def validate_all(devices): tasks = [validate_device(**d) for d in devices] return await asyncio.gather(*tasks, return_exceptions=True) results = await validate_all(device_pairs)
5. 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 asyncio.gather
✓Accept IMEI with dashes/spaces from device settings
✓Return 422 with field-level error messages