OUI-Master-Database

OUI Lookup Examples for Embedded Devices

Example code for looking up MAC address manufacturers using the OUI Master Database API.

API Structure

The API consists of 256 JSON files organized by the first byte (first 2 hex characters) of the OUI:

https://dagnazty.github.io/OUI-Master-Database/api/
├── index.json          # Metadata and available prefixes
├── 00.json             # All OUIs starting with 00:XX:XX
├── 01.json             # All OUIs starting with 01:XX:XX
├── ...
├── 3C.json             # All OUIs starting with 3C:XX:XX (e.g., 3C:D9:2B = HP)
├── ...
└── FF.json             # All OUIs starting with FF:XX:XX

Response Format

Each file contains a JSON object mapping OUI to manufacturer data:

{
  "3C:D9:2B": {
    "m": "Hewlett Packard",
    "s": "HP",
    "t": "Computer",
    "c": "US"
  }
}

Field abbreviations (to minimize payload size):


ESP32 Arduino Example

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

// Base URL for the OUI API
const char* API_BASE = "https://dagnazty.github.io/OUI-Master-Database/api/";

struct OUIResult {
  bool found;
  String manufacturer;
  String shortName;
  String deviceType;
  String country;
};

// Normalize MAC to OUI format (XX:XX:XX)
String macToOUI(String mac) {
  // Remove separators and convert to uppercase
  String clean = "";
  for (int i = 0; i < mac.length(); i++) {
    char c = mac.charAt(i);
    if (isxdigit(c)) {
      clean += toupper(c);
    }
  }
  
  // Take first 6 characters and format
  if (clean.length() >= 6) {
    return clean.substring(0, 2) + ":" + 
           clean.substring(2, 4) + ":" + 
           clean.substring(4, 6);
  }
  return "";
}

// Get first byte prefix for API lookup
String getPrefix(String mac) {
  String clean = "";
  for (int i = 0; i < mac.length(); i++) {
    char c = mac.charAt(i);
    if (isxdigit(c)) {
      clean += toupper(c);
    }
  }
  if (clean.length() >= 2) {
    return clean.substring(0, 2);
  }
  return "";
}

// Lookup OUI in the API
OUIResult lookupOUI(String mac) {
  OUIResult result = {false, "", "", "", ""};
  
  String prefix = getPrefix(mac);
  String oui = macToOUI(mac);
  
  if (prefix.length() == 0 || oui.length() == 0) {
    Serial.println("Invalid MAC address");
    return result;
  }
  
  String url = String(API_BASE) + prefix + ".json";
  
  HTTPClient http;
  http.begin(url);
  http.setTimeout(10000);
  
  int httpCode = http.GET();
  
  if (httpCode == 200) {
    String payload = http.getString();
    
    // Parse JSON - allocate enough memory for ~300-500 entries
    DynamicJsonDocument doc(65536);  // 64KB should be enough for most prefix files
    DeserializationError error = deserializeJson(doc, payload);
    
    if (!error) {
      JsonObject entry = doc[oui];
      if (!entry.isNull()) {
        result.found = true;
        result.manufacturer = entry["m"].as<String>();
        result.shortName = entry["s"].as<String>();
        result.deviceType = entry["t"].as<String>();
        result.country = entry["c"].as<String>();
      }
    } else {
      Serial.print("JSON parse error: ");
      Serial.println(error.c_str());
    }
  } else {
    Serial.print("HTTP error: ");
    Serial.println(httpCode);
  }
  
  http.end();
  return result;
}

void setup() {
  Serial.begin(115200);
  
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nConnected!");
  
  // Test lookups
  String testMACs[] = {
    "00:00:0C:12:34:56",  // Cisco
    "3C:D9:2B:AA:BB:CC",  // HP
    "AC:DE:48:11:22:33",  // Apple
    "24:18:C6:44:55:66"   // Espressif (ESP32)
  };
  
  for (int i = 0; i < 4; i++) {
    Serial.println("\n---------------------------------");
    Serial.print("Looking up: ");
    Serial.println(testMACs[i]);
    
    OUIResult result = lookupOUI(testMACs[i]);
    
    if (result.found) {
      Serial.print("Manufacturer: ");
      Serial.println(result.manufacturer);
      if (result.shortName.length() > 0) {
        Serial.print("Short Name: ");
        Serial.println(result.shortName);
      }
      if (result.deviceType.length() > 0) {
        Serial.print("Device Type: ");
        Serial.println(result.deviceType);
      }
      if (result.country.length() > 0) {
        Serial.print("Country: ");
        Serial.println(result.country);
      }
    } else {
      Serial.println("Not found in database");
    }
  }
}

void loop() {
  // Your main code here
  delay(1000);
}

ESP32 Notes


Raspberry Pi Python Example

#!/usr/bin/env python3
"""
OUI Lookup for Raspberry Pi
Uses the OUI Master Database static API
"""

import requests
import json
import re
from functools import lru_cache

API_BASE = "https://dagnazty.github.io/OUI-Master-Database/api/"

# Cache API responses to avoid repeated requests
@lru_cache(maxsize=256)
def fetch_prefix_data(prefix: str) -> dict:
    """Fetch and cache OUI data for a given prefix."""
    url = f"{API_BASE}{prefix}.json"
    try:
        response = requests.get(url, timeout=10)
        response.raise_for_status()
        return response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error fetching {url}: {e}")
        return {}

def normalize_mac(mac: str) -> tuple:
    """
    Normalize MAC address to OUI format.
    Returns (prefix, oui) tuple.
    
    Accepts formats:
    - 00:00:0C:12:34:56
    - 00-00-0C-12-34-56
    - 00000C123456
    - 00:00:0C (just OUI)
    """
    # Remove all non-hex characters
    clean = re.sub(r'[^0-9A-Fa-f]', '', mac).upper()
    
    if len(clean) < 6:
        return None, None
    
    # Get prefix (first 2 chars) and OUI (first 3 bytes formatted)
    prefix = clean[:2]
    oui = f"{clean[0:2]}:{clean[2:4]}:{clean[4:6]}"
    
    return prefix, oui

def lookup_oui(mac: str) -> dict:
    """
    Look up manufacturer information for a MAC address.
    
    Returns dict with:
    - found: bool
    - manufacturer: str
    - short_name: str
    - device_type: str
    - country: str
    """
    result = {
        'found': False,
        'manufacturer': None,
        'short_name': None,
        'device_type': None,
        'country': None,
        'oui': None
    }
    
    prefix, oui = normalize_mac(mac)
    if not prefix or not oui:
        return result
    
    result['oui'] = oui
    
    # Fetch the prefix file
    data = fetch_prefix_data(prefix)
    
    if oui in data:
        entry = data[oui]
        result['found'] = True
        result['manufacturer'] = entry.get('m')
        result['short_name'] = entry.get('s')
        result['device_type'] = entry.get('t')
        result['country'] = entry.get('c')
    
    return result

def batch_lookup(macs: list) -> list:
    """Look up multiple MAC addresses efficiently."""
    return [lookup_oui(mac) for mac in macs]

# Example usage
if __name__ == "__main__":
    test_macs = [
        "00:00:0C:12:34:56",  # Cisco
        "3C:D9:2B:AA:BB:CC",  # HP  
        "AC:DE:48:11:22:33",  # Apple
        "24:18:C6:44:55:66",  # Espressif (ESP32)
        "B8:27:EB:AA:BB:CC",  # Raspberry Pi
        "DC:A6:32:11:22:33",  # Raspberry Pi
        "E4:5F:01:AA:BB:CC",  # Raspberry Pi
    ]
    
    print("OUI Master Database Lookup")
    print("=" * 50)
    
    for mac in test_macs:
        result = lookup_oui(mac)
        print(f"\nMAC: {mac}")
        print(f"OUI: {result['oui']}")
        
        if result['found']:
            print(f"  Manufacturer: {result['manufacturer']}")
            if result['short_name']:
                print(f"  Short Name:   {result['short_name']}")
            if result['device_type']:
                print(f"  Device Type:  {result['device_type']}")
            if result['country']:
                print(f"  Country:      {result['country']}")
        else:
            print("  Not found in database")
    
    print("\n" + "=" * 50)
    print("Cache info:", fetch_prefix_data.cache_info())

Installing Dependencies

pip install requests

Raspberry Pi Notes


Offline Mode (Download All Data)

For devices with storage but limited/no internet:

Download Script (Bash)

#!/bin/bash
# Download all API files for offline use

mkdir -p oui_api
cd oui_api

# Download index
curl -sO https://ringmast4r.github.io/OUI-Master-Database/api/index.json

# Download all prefix files (00-FF)
for i in $(seq 0 255); do
  prefix=$(printf "%02X" $i)
  echo "Downloading $prefix.json..."
  curl -sf "https://ringmast4r.github.io/OUI-Master-Database/api/$prefix.json" -o "$prefix.json" 2>/dev/null || true
done

echo "Done! Files saved to oui_api/"

Using Offline Data (Python)

import json
import os

OUI_DIR = "./oui_api"

def lookup_offline(mac: str) -> dict:
    """Offline lookup using downloaded files."""
    clean = ''.join(c for c in mac.upper() if c in '0123456789ABCDEF')
    if len(clean) < 6:
        return {'found': False}
    
    prefix = clean[:2]
    oui = f"{clean[0:2]}:{clean[2:4]}:{clean[4:6]}"
    
    filepath = os.path.join(OUI_DIR, f"{prefix}.json")
    if not os.path.exists(filepath):
        return {'found': False}
    
    with open(filepath) as f:
        data = json.load(f)
    
    if oui in data:
        entry = data[oui]
        return {
            'found': True,
            'manufacturer': entry.get('m'),
            'device_type': entry.get('t'),
            'country': entry.get('c')
        }
    
    return {'found': False}

API Endpoints

Endpoint Description Size
/api/index.json Metadata, available prefixes ~5 KB
/api/00.json OUIs starting with 00:XX:XX ~50 KB
/api/3C.json OUIs starting with 3C:XX:XX ~40 KB
~30-80 KB each

License

The API data is compiled from IEEE (public domain), Wireshark (GPLv2), and Nmap (Modified GPLv2). See the main repository LICENSE for details.

Credits