Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.1.0
info:
title: macadress.com API
version: "1.0"
description: >
MAC address / OUI vendor lookup. Every field on the lookup response
beyond the original v1 set (see the x-added-in-version extension on
each) is additive and nullable: existing integrations reading only the
original fields are unaffected by new ones appearing alongside them.
contact:
url: https://macadress.com/docs
license:
name: See https://macadress.com/terms
externalDocs:
description: Full documentation with request/response examples
url: https://macadress.com/docs
servers:
- url: https://api.macadress.com
security:
- apiKeyHeader: []
- apiKeyQuery: []
paths:
/v1/mac/{mac}:
get:
operationId: lookupMAC
summary: Look up a single MAC address
parameters:
- name: mac
in: path
required: true
schema: { type: string }
description: >
Any common format: colon, dash, dot-grouped, space-separated, or
bare hex (case-insensitive).
example: "00:03:93:AB:12:34"
responses:
"200":
description: Lookup result (present whether or not the address is registered)
headers:
X-Request-Id: { $ref: "#/components/headers/XRequestId" }
X-Data-Version: { $ref: "#/components/headers/XDataVersion" }
X-RateLimit-Limit: { $ref: "#/components/headers/XRateLimitLimit" }
X-RateLimit-Remaining: { $ref: "#/components/headers/XRateLimitRemaining" }
X-RateLimit-Reset: { $ref: "#/components/headers/XRateLimitReset" }
content:
application/json:
schema: { $ref: "#/components/schemas/Result" }
"400":
description: Input did not parse as a MAC address
content:
application/json:
schema: { $ref: "#/components/schemas/ParseErrorBody" }
"401": { $ref: "#/components/responses/Unauthorized" }
"429": { $ref: "#/components/responses/RateLimited" }
/v1/mac/batch:
post:
operationId: lookupMACBatch
summary: Look up up to 100 MAC addresses in one request
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [macs]
properties:
macs:
type: array
maxItems: 100
items: { type: string }
api_key:
type: string
description: Alternative to the Authorization header or ?api_key= query param.
responses:
"200":
description: One result per input, in the same order, each with the same shape as GET /v1/mac/{mac}
headers:
X-Request-Id: { $ref: "#/components/headers/XRequestId" }
X-Data-Version: { $ref: "#/components/headers/XDataVersion" }
X-RateLimit-Limit: { $ref: "#/components/headers/XRateLimitLimit" }
X-RateLimit-Remaining: { $ref: "#/components/headers/XRateLimitRemaining" }
X-RateLimit-Reset: { $ref: "#/components/headers/XRateLimitReset" }
content:
application/json:
schema:
type: object
properties:
count: { type: integer }
results:
type: array
items: { $ref: "#/components/schemas/BatchItem" }
"400":
description: Malformed request body, empty macs array, or more than 100 entries
content:
application/json:
schema: { $ref: "#/components/schemas/ParseErrorBody" }
"401": { $ref: "#/components/responses/Unauthorized" }
"429": { $ref: "#/components/responses/RateLimited" }
/v1/vendors:
get:
operationId: searchVendors
summary: Search the registered vendor/block directory
parameters:
- name: query
in: query
schema: { type: string }
description: Substring match against organization name, case-insensitive.
- name: country
in: query
schema: { type: string }
description: Exact ISO 3166-1 alpha-2 code, e.g. US, DE, JP.
- name: limit
in: query
schema: { type: integer, default: 10, maximum: 50 }
description: Max results to return.
responses:
"200":
description: Matching non-private blocks. Counts as one call against the plan quota.
headers:
X-Request-Id: { $ref: "#/components/headers/XRequestId" }
X-Data-Version: { $ref: "#/components/headers/XDataVersion" }
X-RateLimit-Limit: { $ref: "#/components/headers/XRateLimitLimit" }
X-RateLimit-Remaining: { $ref: "#/components/headers/XRateLimitRemaining" }
X-RateLimit-Reset: { $ref: "#/components/headers/XRateLimitReset" }
content:
application/json:
schema: { $ref: "#/components/schemas/VendorSearchResult" }
"401": { $ref: "#/components/responses/Unauthorized" }
"429": { $ref: "#/components/responses/RateLimited" }
/v1/healthz:
get:
operationId: healthz
summary: Liveness check (not counted against any quota, no API key required)
security: []
responses:
"200":
description: Database reachable
content:
application/json:
schema:
type: object
properties: { status: { type: string, enum: [ok] } }
"503":
description: Database unreachable
content:
application/json:
schema:
type: object
properties: { error: { type: string } }
components:
securitySchemes:
apiKeyHeader:
type: http
scheme: bearer
description: 'Authorization: Bearer mk_...'
apiKeyQuery:
type: apiKey
in: query
name: api_key
headers:
XRequestId:
description: Per-request identifier, present on every response including errors.
schema: { type: string }
XDataVersion:
description: Date (UTC) the underlying vendor database last synced successfully. Same value as meta.database_version.
schema: { type: string, format: date }
XRateLimitLimit:
description: Requests-per-minute cap for the authenticated plan.
schema: { type: integer }
XRateLimitRemaining:
description: Requests remaining in the current sliding window.
schema: { type: integer }
XRateLimitReset:
description: Seconds until the current window's usage resets.
schema: { type: integer }
responses:
Unauthorized:
description: Missing or invalid API key
content:
application/json:
schema:
type: object
properties: { error: { type: string } }
RateLimited:
description: Requests-per-minute limit exceeded for the authenticated plan
content:
application/json:
schema:
type: object
properties: { error: { type: string } }
schemas:
ParseErrorBody:
type: object
properties:
valid: { type: boolean, enum: [false] }
error: { type: string }
request_id: { type: string }
ConfidenceNoneLowMediumHigh:
type: string
enum: [none, low, medium, high]
VendorBlock:
type: object
description: One IEEE-assigned MAC/OUI prefix block, as returned by /v1/vendors.
properties:
prefix_int: { type: integer, description: The block's registered prefix, left-justified into the high bits of a 48-bit value. }
mask_bits: { type: integer, enum: [24, 28, 36] }
block_type: { type: string, enum: [MA-L, MA-M, MA-S, IAB, CID] }
organization: { type: string }
address: { type: string }
country: { type: string, description: ISO 3166-1 alpha-2, empty if not extracted. }
is_private: { type: boolean }
first_seen_at: { type: string, format: date-time }
last_changed_at: { type: string, format: date-time }
VendorSearchResult:
type: object
properties:
total: { type: integer, description: How many blocks match, independent of the limit applied. }
blocks:
type: array
items: { $ref: "#/components/schemas/VendorBlock" }
DeviceCategory:
type: string
description: >
Controlled taxonomy. "unknown" when the vendor is too broad or no
vendor profile exists in this deployment's starter dataset: most
registered organizations fall here today.
enum:
- computer
- smartphone
- tablet
- router
- switch
- wireless_access_point
- firewall
- server
- storage
- printer
- camera
- smart_tv
- media_device
- gaming_console
- iot
- embedded_device
- industrial
- medical
- automotive
- virtual_machine
- container
- network_interface
- consumer_electronics
- unknown
Result:
type: object
description: >
The x-added-in-version extension on a property marks it as added
after the original v1 shape shipped; every such field is nullable
or safely defaulted.
required:
- mac
- valid
- oui
- registered
- transmission_type
- administration_type
- locally_administered
- potentially_randomized
- randomization_confidence
- vendor_lookup_reliable
- is_zero
- is_broadcast
- explanation
- is_private
- device
- virtualization
- special_use
- vendor_location
- randomization
- local_vendor_derivation
- meta
properties:
mac: { type: string, description: Canonical colon-separated uppercase form. }
valid: { type: boolean }
oui:
type: string
description: Always the first 24 bits of the address itself, regardless of matched block width. See matched_prefix for the complete matched registry block.
organization: { type: [string, "null"] }
vendor_address: { type: [string, "null"] }
country: { type: [string, "null"], description: ISO 3166-1 alpha-2, extracted from vendor_address. }
block_type: { type: [string, "null"], enum: [MA-L, MA-M, MA-S, IAB, CID, null] }
address_capacity: { type: [integer, "null"] }
range_start: { type: [string, "null"] }
range_end: { type: [string, "null"] }
registered: { type: boolean }
transmission_type: { type: string, enum: [unicast, multicast, broadcast] }
administration_type: { type: string, enum: [universally_administered, locally_administered] }
locally_administered: { type: boolean }
slap_quadrant: { type: [string, "null"], enum: [AAI, ELI, reserved, SAI, null] }
eui64: { type: [string, "null"] }
ipv6_link_local: { type: [string, "null"] }
potentially_randomized: { type: boolean }
randomization_confidence: { type: string, enum: [none, possible, likely] }
vendor_lookup_reliable: { type: boolean }
is_zero: { type: boolean }
is_broadcast: { type: boolean }
explanation: { type: string }
is_private: { type: boolean, deprecated: true, description: "Use organization === null and vendor_lookup_reliable instead." }
matched_prefix:
type: [string, "null"]
x-added-in-version: "1.1"
description: The complete registered prefix actually matched, at its real width (colon-separated bytes plus a trailing bare hex nibble for a non-byte-aligned width like /28 or /36). Null when unregistered.
prefix_length:
type: [integer, "null"]
x-added-in-version: "1.1"
registry:
x-added-in-version: "1.1"
oneOf:
- type: "null"
- type: object
properties:
source: { type: string, enum: [IEEE] }
record_type: { type: string, enum: [MA-L, MA-M, MA-S, IAB, CID] }
record_updated_at:
type: [string, "null"]
format: date
description: "Always null today: IEEE's public feeds carry no per-record update-date column."
database_synced_at: { type: [string, "null"], format: date-time }
device:
x-added-in-version: "1.1"
type: object
properties:
category: { $ref: "#/components/schemas/DeviceCategory" }
possible_categories: { type: array, items: { $ref: "#/components/schemas/DeviceCategory" } }
confidence: { $ref: "#/components/schemas/ConfidenceNoneLowMediumHigh" }
inference_source: { type: [string, "null"], enum: [vendor_profile, null] }
exact_model_known: { type: boolean, enum: [false], description: "Always false: a MAC address alone never identifies an exact model." }
virtualization:
x-added-in-version: "1.1"
type: object
properties:
detected: { type: boolean }
platform: { type: [string, "null"] }
confidence: { type: string, enum: [none, low, high, exact] }
signals: { type: array, items: { type: string } }
special_use:
x-added-in-version: "1.1"
type: object
properties:
detected: { type: boolean }
type: { type: [string, "null"] }
protocol: { type: [string, "null"] }
name: { type: [string, "null"] }
source: { type: [string, "null"] }
confidence: { type: string, enum: [none, high, exact] }
vendor_location:
x-added-in-version: "1.1"
type: object
properties:
raw: { type: [string, "null"] }
address_line: { type: [string, "null"] }
city: { type: [string, "null"] }
region: { type: [string, "null"] }
postal_code: { type: [string, "null"] }
country_code: { type: [string, "null"] }
country_name: { type: [string, "null"] }
parse_confidence: { type: string, enum: [none, low, medium] }
randomization:
x-added-in-version: "1.1"
type: object
description: Machine-readable explanation of potentially_randomized/randomization_confidence, cross-checked against virtualization.
properties:
potentially_randomized: { type: boolean }
confidence: { type: string, enum: [none, possible, likely] }
signals: { type: array, items: { type: string } }
alternative_explanations: { type: array, items: { type: string } }
local_vendor_derivation:
x-added-in-version: "1.2"
type: object
description: >
Best-effort vendor recovery for a locally administered unicast
address that looks formed by setting the U/L bit on a real IEEE
assignment (multi-BSSID Wi-Fi APs, Wi-Fi Direct / P2P
interfaces, naive MAC spoofing), rather than by OS privacy
randomization. detected is true only when clearing the U/L bit
yields a prefix that matches a registered block, the address is
not already registered, it is unicast, it is not an
SAI-quadrant protocol address, and its suffix is not all zero.
confidence is always "low": a match is strong evidence the bits
came from that OUI, but cannot tell an AP's own BSSID from a
spoofer who chose that OUI. This is never a decode of OS
randomization, whose 46 bits are uniformly random.
properties:
detected: { type: boolean }
method: { type: string, enum: ["", ul_bit_flip] }
universal_mac:
type: [string, "null"]
description: The address actually looked up, i.e. this address with the U/L bit cleared. Null when detected is false.
confidence: { type: string, enum: [none, low] }
organization: { type: [string, "null"], description: Registered organization behind the cleared prefix. Null when the block is private or nothing was derived. }
country: { type: [string, "null"], description: ISO 3166-1 alpha-2 of the derived block. }
matched_prefix: { type: [string, "null"] }
block_type: { type: [string, "null"], enum: [MA-L, MA-M, MA-S, IAB, CID, null] }
lookup_url: { type: [string, "null"], format: uri }
device:
oneOf:
- type: "null"
- type: object
description: enrich device-category inference run against the derived organization; same shape as the top-level device object.
properties:
category: { $ref: "#/components/schemas/DeviceCategory" }
possible_categories: { type: array, items: { $ref: "#/components/schemas/DeviceCategory" } }
confidence: { $ref: "#/components/schemas/ConfidenceNoneLowMediumHigh" }
inference_source: { type: [string, "null"], enum: [vendor_profile, null] }
exact_model_known: { type: boolean, enum: [false] }
signals: { type: array, items: { type: string } }
assignment:
x-added-in-version: "1.1"
oneOf:
- type: "null"
- type: object
properties:
registered_at:
type: [string, "null"]
description: "Always null: no authoritative IEEE registration date exists in the source feed."
first_seen_at: { type: [string, "null"], format: date, description: When this deployment first observed the record, not an IEEE date. }
last_changed_at: { type: [string, "null"], format: date }
vendor:
x-added-in-version: "1.1"
description: "Conservative canonical identity: a mechanical slug of the exact registered name, never a merge of spelling variants or subsidiaries."
oneOf:
- type: "null"
- type: object
properties:
id: { type: string }
registered_name: { type: string }
canonical_name: { type: string }
slug: { type: string }
block_count: { type: integer }
lookup_url: { type: string, format: uri }
meta:
x-added-in-version: "1.1"
type: object
properties:
request_id: { type: string }
api_version: { type: string, enum: ["v1"] }
database_version: { type: [string, "null"], format: date }
processed_at: { type: string, format: date-time }
cached: { type: boolean }
BatchItem:
allOf:
- { $ref: "#/components/schemas/Result" }
- type: object
properties:
input: { type: string, description: The original input string as submitted. }
error: { type: string, description: Present only when this entry failed to resolve. }