Reference

API documentation.

Three endpoints, plain JSON, no SDK required. Everything below works today against https://api.macadress.com. Machine-readable: OpenAPI 3.1 spec.

v1.1 response fields: the response now includes matched_prefix, registry, device, virtualization, special_use, vendor_location, randomization, assignment, vendor, and meta. Every one of these is new since the original v1 shape shipped, and every one is nullable or safely defaulted: no existing field was removed, renamed, or changed in meaning, so an integration reading only the original fields keeps working unchanged.
Early access: every request needs a valid API key. Both the requests/minute limit and cycle quota below are enforced for every plan; Growth and Scale get a 20% overage grace before pausing. See pricing for plan details.

Authentication

Sign up for a free account and you'll get a key instantly, visible any time on your account page. Send it any one of three ways, whichever fits your client best:

MethodHow
HeaderWorks for every endpoint: Authorization: Bearer mk_...
Query paramWorks for every endpoint: ?api_key=mk_...
POST bodyBatch endpoint only: an "api_key" field alongside "macs" in the JSON body.

A missing or invalid key gets a 401:

{"error": "missing API key: pass it as \"api_key\" (query param or POST body) or an Authorization: Bearer header"}

Rate limits

Each plan gets a requests-per-minute budget, shared across both lookup endpoints (a batch request counts once, not once per address). It resets on a rolling window, not a fixed clock minute.

PlanRequests / minute
Free30
Growth120
Scale600

Over the limit gets a 429:

{"error": "rate limit exceeded: 30 requests/minute on the free plan"}

Cycle quota

Every account has a 30-day usage cycle that starts the moment you sign up and rolls forward automatically, not a calendar month, so it doesn't matter what day you join. Requests draw down that cycle's lookup budget; a batch request counts however many addresses it actually looked up, not 1 per request. Free stops exactly at quota. Growth and Scale get a 20% grace buffer on top of their quota before they're paused too — grace used isn't forgiven, it carries into the next cycle as a smaller starting budget instead of a bill.

PlanLookups / cycleGracePaused at
Free1,0001,000
Growth100,00020%120,000
Scale1,000,00020%1,200,000

Over quota on the free plan:

{"error": "quota exceeded: 1000 lookups on the free plan this cycle, upgrade at /pricing for more"}

Past quota plus grace on a paid plan:

{"error": "quota exceeded: used your 100000-lookup growth plan quota plus its 20% grace buffer this cycle, resets at renewal"}

Usage counts are held in the API's memory and flushed to storage roughly once a minute, so the count and reset date shown on your account page can lag live usage by up to that long.

Response headers

Every response, success or error, carries these:

HeaderMeaning
X-Request-IdPer-request identifier. Matches the request_id in the JSON body on a successful lookup.
X-Data-VersionDate (UTC) the underlying vendor database last synced successfully, e.g. 2026-08-22. Same value as meta.database_version.
X-RateLimit-LimitRequests-per-minute cap for your plan.
X-RateLimit-RemainingRequests left in the current sliding window.
X-RateLimit-ResetSeconds until the current window's usage resets.

Formats accepted

The :mac path segment and every address inside a batch request accept colons, dashes, dots, spaces, or no separator at all: AA:BB:CC:DD:EE:FF, AA-BB-CC-DD-EE-FF, AA BB CC DD EE FF, aabb.ccdd.eeff, or AABBCCDDEEFF. Case doesn't matter.

GET /v1/mac/:mac

Look up a single MAC address.

Request

curl https://api.macadress.com/v1/mac/00:03:93:AB:12:34 \
  -H "Authorization: Bearer mk_your_api_key"

Response 200

{
  "mac": "00:03:93:AB:12:34",
  "valid": true,
  "oui": "00:03:93",
  "organization": "Apple, Inc.",
  "vendor_address": "1 Infinite Loop Cupertino CA US 95014",
  "country": "US",
  "block_type": "MA-L",
  "address_capacity": 16777216,
  "range_start": "00:03:93:00:00:00",
  "range_end": "00:03:93:FF:FF:FF",
  "registered": true,
  "transmission_type": "unicast",
  "administration_type": "universally_administered",
  "locally_administered": false,
  "slap_quadrant": null,
  "eui64": "02:03:93:FF:FE:AB:12:34",
  "ipv6_link_local": "fe80::203:93ff:feab:1234",
  "potentially_randomized": false,
  "randomization_confidence": "none",
  "vendor_lookup_reliable": true,
  "is_zero": false,
  "is_broadcast": false,
  "explanation": "This is a universally administered unicast address with a registered vendor prefix.",
  "is_private": false,
  "matched_prefix": "00:03:93",
  "prefix_length": 24,
  "registry": {
    "source": "IEEE",
    "record_type": "MA-L",
    "record_updated_at": null,
    "database_synced_at": "2026-08-22T03:10:00Z"
  },
  "device": {
    "category": "consumer_electronics",
    "possible_categories": ["smartphone", "tablet", "computer", "media_device"],
    "confidence": "medium",
    "inference_source": "vendor_profile",
    "exact_model_known": false
  },
  "virtualization": { "detected": false, "platform": null, "confidence": "none", "signals": [] },
  "special_use": { "detected": false, "type": null, "protocol": null, "name": null, "source": null, "confidence": "none" },
  "vendor_location": {
    "raw": "1 Infinite Loop Cupertino CA US 95014",
    "address_line": null,
    "city": null,
    "region": null,
    "postal_code": null,
    "country_code": "US",
    "country_name": "United States",
    "parse_confidence": "low"
  },
  "randomization": { "potentially_randomized": false, "confidence": "none", "signals": [], "alternative_explanations": [] },
  "assignment": { "registered_at": null, "first_seen_at": "2026-08-22", "last_changed_at": "2026-08-22" },
  "vendor": {
    "id": "apple-inc",
    "registered_name": "Apple, Inc.",
    "canonical_name": "Apple, Inc.",
    "slug": "apple-inc",
    "block_count": 1553,
    "lookup_url": "https://macadress.com/vendor/000393"
  },
  "meta": {
    "request_id": "5249ebff-787d-4776-97c3-235a8c95823c",
    "api_version": "v1",
    "database_version": "2026-08-22",
    "processed_at": "2026-08-22T10:26:47Z",
    "cached": false
  }
}

vendor_location's address_line/city/region/postal_code only get filled in for the minority of IEEE addresses that use a clean comma-delimited format; the far more common space-only shape shown above yields just raw and country_code at "low" confidence rather than guessing where the street address ends and the city begins.

Invalid input 400

{"valid": false, "error": "invalid MAC address", "request_id": "19ee676c-4410-443f-813e-3bbb3433b4bf"}
POST /v1/mac/batch

Look up up to 100 addresses in one request. Invalid entries don't fail the whole batch, each result reports its own status.

Request

curl -X POST https://api.macadress.com/v1/mac/batch \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer mk_your_api_key" \
  -d '{"macs": ["00:03:93:AB:12:34", "not-a-mac"]}'

# or with the key inline in the body instead of the header:
curl -X POST https://api.macadress.com/v1/mac/batch \
  -H "Content-Type: application/json" \
  -d '{"api_key": "mk_your_api_key", "macs": ["00:03:93:AB:12:34"]}'

Response 200 (abbreviated: each successful entry has every field shown in GET /v1/mac/:mac's example above)

{
  "count": 2,
  "results": [
    {
      "input": "00:03:93:AB:12:34",
      "mac": "00:03:93:AB:12:34",
      "valid": true,
      "organization": "Apple, Inc.",
      "matched_prefix": "00:03:93",
      "meta": { "request_id": "d204feb2-...", "api_version": "v1", "database_version": "2026-08-22", "processed_at": "2026-08-22T10:27:51Z", "cached": false }
    },
    {
      "input": "not-a-mac",
      "error": "invalid MAC address",
      "mac": "",
      "valid": false,
      "organization": null,
      "matched_prefix": null,
      "meta": { "request_id": "d204feb2-...", "api_version": "v1", "database_version": "2026-08-22", "processed_at": "2026-08-22T10:27:51Z", "cached": false }
    }
  ]
}

Each entry in results has the same shape as the single-lookup response, plus input (what you sent) and, on failure, error. Every item in one batch call shares the same meta.request_id: it's one HTTP request, even though it billed against your quota per address looked up.

GET /v1/healthz

Returns 200 {"status": "ok"} when the database is reachable, 503 otherwise. For uptime checks, not counted against any quota.

Response fields

FieldMeaning
macThe address, normalized to canonical colon-separated uppercase form.
validWhether the input parsed as a MAC address at all.
ouiThe top 24 bits (first three octets), colon-separated.
organizationRegistered vendor name, or null if unregistered or the block is marked private by IEEE.
vendor_addressThe organization's registered address, or null.
countryISO 3166-1 alpha-2 code extracted from the vendor address, or null.
block_typeIEEE registry type: MA-L, MA-M, MA-S, IAB, or CID, or null if unregistered.
address_capacityThe address capacity of the matched block: how many addresses it covers, not how many devices are actually manufactured or active.
range_start / range_endFirst and last address in the matched block.
registeredWhether the address falls in a block IEEE has assigned.
transmission_typeunicast, multicast, or broadcast, from the I/G bit.
administration_typeuniversally_administered or locally_administered, from the U/L bit.
locally_administeredBoolean form of the same U/L bit.
slap_quadrantIEEE 802c SLAP quadrant when locally administered (null otherwise).
eui64The modified EUI-64 mathematically derived from this MAC, or null for non-unicast addresses. This is a derivation from the input, not confirmation the device uses it.
ipv6_link_localThe IPv6 link-local address SLAAC would derive from this MAC, or null for non-unicast addresses. Same caveat as eui64: derived, not confirmed live.
potentially_randomizedWhether this looks like a privacy-randomized address rather than a factory-assigned one.
randomization_confidencenone, possible, or likely.
vendor_lookup_reliableWhether organization can be trusted as the manufacturer, false for randomized or unregistered addresses.
is_zeroWhether the address is all zeros.
is_broadcastWhether the address is the broadcast address (all ones).
explanationThe same plain-English sentence shown on the website's result page.
is_privateDeprecated: kept for older clients. Use organization === null and vendor_lookup_reliable instead.
matched_prefixThe complete registered prefix actually matched, at its real width: full colon-separated bytes, plus a trailing bare hex nibble when the block isn't byte-aligned (a /28 or /36 match). null when unregistered. Unlike oui (always the first 24 bits of the address), this reflects the block's actual size.
prefix_lengthBit width of the matched block: 24 for MA-L/IAB/CID, 28 for MA-M, 36 for MA-S. null when unregistered.
registrynull when unregistered, otherwise {source, record_type, record_updated_at, database_synced_at}. record_updated_at is always null: IEEE's public feeds don't publish a per-record update date. database_synced_at is when this deployment's own copy last synced.
device{category, possible_categories, confidence, inference_source, exact_model_known}. Inferred from a small, manually curated vendor→category dataset, not from IEEE data (which has no product-category field at all). category is "unknown" and confidence is "none" for any organization not in that dataset — most of the registry today. exact_model_known is always false: a MAC address alone never identifies an exact model.
virtualization{detected, platform, confidence, signals}. "exact" confidence only for prefixes IEEE itself registered to a hypervisor vendor (VMware, Xen, Hyper-V, VirtualBox, Parallels); lower confidence for conventions that aren't IEEE-registered assignments (QEMU/KVM's libvirt default, Docker's bridge driver). A locally administered address alone is never enough on its own — see randomization.
special_use{detected, type, protocol, name, source, confidence}. Reserved/protocol addresses (broadcast, IPv4/IPv6 multicast mapping, VRRP, HSRP, STP, LACP, 802.1X, LLDP, and a few others), each with the IEEE/IETF/vendor source it's documented in. All fields null and confidence "none" when nothing matches.
vendor_location{raw, address_line, city, region, postal_code, country_code, country_name, parse_confidence}. Every component beyond raw is nullable; only the minority of addresses with a clean comma-delimited shape get a full breakdown ("medium" confidence). The common space-only shape yields just raw/country_code at "low" confidence rather than guessing.
randomization{potentially_randomized, confidence, signals, alternative_explanations}. Machine-readable version of the top-level potentially_randomized/randomization_confidence fields, cross-checked against virtualization (e.g. a locally administered address matching a known hypervisor prefix lists virtual_machine/container as alternative explanations, not just randomization).
assignmentnull when unregistered, otherwise {registered_at, first_seen_at, last_changed_at}. registered_at is always null (no authoritative IEEE registration date exists). first_seen_at/last_changed_at are this deployment's own sync history, not IEEE dates — don't treat either as the age of a device using this prefix.
vendornull when unregistered, otherwise {id, registered_name, canonical_name, slug, block_count, lookup_url}. id/slug are a mechanical slug of the exact registered name — never a merge of spelling variants, subsidiaries, or acquisitions of the same real-world company.
meta{request_id, api_version, database_version, processed_at, cached}, present on every successful response (and request_id alone on error bodies). Mirrors the X-Request-Id/X-Data-Version response headers.