/api/ip/inspectMCP tool ip_inspectParses an IPv4 or IPv6 address in any spelling and returns its canonical form (RFC 5952 for IPv6), the full expanded form and integer value, any IPv4 address embedded in an IPv6 address (IPv4-mapped, IPv4-compatible, IPv4-translated, NAT64 64:ff9b::/96 and 64:ff9b:1::/48, 6to4, Teredo), every range it falls in from the IANA IPv4 and IPv6 Special-Purpose Address Registries (with their source, destination, forwardable, globally-reachable and reserved columns), and whether it is globally reachable. An embedded IPv4 address is checked too, so ::ffff:a00:1 is reported as private-use 10.0.0.1. Non-standard IPv4 spellings that browsers and inet_aton accept (127.1, 0x7f.0.0.1, 0177.0.0.1, 2130706433) are read the way they are and flagged. A /prefix adds the network, and cidrs tests membership. Nothing is resolved or contacted.
Browser version (runs locally): IP Address Inspector.
Try it
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
addressrequired | string (≤ 104 chars) | — | An IPv4 or IPv6 address in any spelling. Optional: [brackets], an IPv6 zone ID (fe80::1%eth0), or a /prefix to describe the network as well. |
cidrs | list of strings | — | Comma-separated CIDRs (IPv4 or IPv6, up to 100) to test the address against, e.g. 10.0.0.0/8,fc00::/7. An IPv6 address also matches an IPv4 CIDR through its embedded IPv4 address. |
Also accepted on every endpoint: format (json | text). Parameters may come from the query string, a JSON object body, an urlencoded or multipart form, or (for input) a text/plain body.
Global parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
format | json | text | json | json returns the documented object. text returns only the primary value as text/plain (see "Text output"). |
Response 200 application/json
| Field | Type | Description |
|---|---|---|
version | integer | 4 or 6. |
input | string | The input as given. |
canonical | string | Canonical text form: dotted decimal for IPv4; RFC 5952 for IPv6 (lowercase, shortest, embedded IPv4 in dotted form where RFC 5952/6052 recommend it). |
expanded | string | IPv6: all eight groups, four hex digits each. IPv4: dotted decimal. |
zone | string | null | IPv6 zone ID (the part after %), if any. |
integer | string | The address as an unsigned integer, in decimal. |
embedded_ipv4 | string | null | An IPv4 address carried inside the IPv6 address. |
embedding | one of (by kind) | How the IPv4 address is embedded. |
embedding (kind = "undefined") | variant | |
embedding (kind = "undefined") | variant | |
ranges | array of object | Every special-purpose range matched, least specific first. |
ranges[].cidr | string | The registry block, e.g. 10.0.0.0/8. |
ranges[].name | string | The registry's name for it, e.g. Private-Use. |
ranges[].rfc | string | Where it is defined. |
ranges[].registry | "ipv4-special-purpose" | "ipv6-special-purpose" | "ipv4-multicast" | "ipv6-address-space" | Which IANA registry it comes from. |
ranges[].source | one of (by kind) | Valid as a source address (null: N/A or not in a special-purpose registry). |
ranges[].source (kind = "undefined") | variant | |
ranges[].source (kind = "undefined") | variant | |
ranges[].destination | one of (by kind) | Valid as a destination address. |
ranges[].destination (kind = "undefined") | variant | |
ranges[].destination (kind = "undefined") | variant | |
ranges[].forwardable | one of (by kind) | Routers may forward it. |
ranges[].forwardable (kind = "undefined") | variant | |
ranges[].forwardable (kind = "undefined") | variant | |
ranges[].globally_reachable | one of (by kind) | Reachable on the global internet, per the registry. |
ranges[].globally_reachable (kind = "undefined") | variant | |
ranges[].globally_reachable (kind = "undefined") | variant | |
ranges[].reserved_by_protocol | one of (by kind) | Reserved by a protocol specification. |
ranges[].reserved_by_protocol (kind = "undefined") | variant | |
ranges[].reserved_by_protocol (kind = "undefined") | variant | |
ranges[].via | "address" | "embedded_ipv4" | Matched by the address itself, or by the IPv4 address embedded in it. |
globally_reachable | one of (by kind) | Whether the address is globally reachable; false if an embedded IPv4 address is not. null when it depends on scope (multicast). |
globally_reachable (kind = "undefined") | variant | |
globally_reachable (kind = "undefined") | variant | |
network | one of (by kind) | With a /prefix: the network. Otherwise null. |
network (kind = "undefined") | variant | |
network.cidr | string | The network, host bits cleared. |
network.prefix | integer | Prefix length. |
network.first | string | First address. |
network.last | string | Last address (the broadcast address for IPv4). |
network.size | string | Number of addresses, in decimal. |
network.netmask | string | null | IPv4 dotted netmask. |
network (kind = "undefined") | variant | |
cidr_matches | array of object | One entry per cidrs value, in order. |
cidr_matches[].cidr | string | The CIDR as given. |
cidr_matches[].contains | boolean | Whether the address (or its embedded IPv4) is inside it. |
cidr_matches[].via | one of (by kind) | What matched. |
cidr_matches[].via (kind = "undefined") | variant | |
cidr_matches[].via (kind = "undefined") | variant | |
notes | array of string | Non-standard spellings, deprecated forms, host bits set. |
Text output (format=text, text/plain): The canonical form.
Errors
Errors are JSON: {"error": "<message>", "code": "<code>", "param": "<name>"?}.
| Status | Code | When |
|---|---|---|
422 | unprocessable_input | address is not an IPv4 or IPv6 address (the message says why), has a port, a zone ID on IPv4, or a prefix longer than the address. |
400 | invalid_parameter | A cidrs value is not a valid CIDR, or there are more than 100. |
400 | missing_parameter | A required parameter is absent. |
400 | unknown_parameter | A parameter name is not in the table above (typos are rejected, not ignored). |
413 | payload_too_large | The request body exceeds 1 MiB. |
405 | method_not_allowed | A method other than GET, POST or OPTIONS. |
Behaviour
- Idempotent: the same arguments always return the same result.
- Requires a client ID and secret (authentication). Each call is recorded as one metadata row against your client ID; inputs and outputs are never stored or logged. Responses carry
cache-control: no-store. - CORS: any origin may call this endpoint from a browser, using your credentials.
Examples
Responses below were produced by running each request through this code at build time. Long strings are shortened.
IPv4-mapped IPv6 written in hex: a private address in disguise
curl 'https://util.caruana.me/api/ip/inspect?address=%3A%3Affff%3Aa00%3A1'Response 200
{
"version": 6,
"input": "::ffff:a00:1",
"canonical": "::ffff:10.0.0.1",
"expanded": "0000:0000:0000:0000:0000:ffff:0a00:0001",
"zone": null,
"integer": "281470849515521",
"embedded_ipv4": "10.0.0.1",
"embedding": "ipv4-mapped",
"ranges": [
{
"cidr": "::ffff:0:0/96",
"name": "IPv4-mapped Address",
"rfc": "RFC 4291",
"registry": "ipv6-special-purpose",
"source": false,
"destination": false,
"forwardable": false,
"globally_reachable": false,
"reserved_by_protocol": true,
"via": "address"
},
{
"cidr": "10.0.0.0/8",
"name": "Private-Use",
"rfc": "RFC 1918",
"registry": "ipv4-special-purpose",
"source": true,
"destination": true,
"forwardable": true,
"globally_reachable": false,
"reserved_by_protocol": false,
"via": "embedded_ipv4"
}
],
"globally_reachable": false,
"network": null,
"cidr_matches": [],
"notes": []
}Same call as an MCP tools/call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "ip_inspect",
"arguments": {
"address": "::ffff:a00:1"
}
}
}Legacy IPv4 shorthand, as an SSRF payload would write it
curl 'https://util.caruana.me/api/ip/inspect?address=0x7f.1&cidrs=127.0.0.0%2F8%2C10.0.0.0%2F8'Response 200
{
"version": 4,
"input": "0x7f.1",
"canonical": "127.0.0.1",
"expanded": "127.0.0.1",
"zone": null,
"integer": "2130706433",
"embedded_ipv4": null,
"embedding": null,
"ranges": [
{
"cidr": "127.0.0.0/8",
"name": "Loopback",
"rfc": "RFC 1122 §3.2.1.3",
"registry": "ipv4-special-purpose",
"source": false,
"destination": false,
"forwardable": false,
"globally_reachable": false,
"reserved_by_protocol": true,
"via": "address"
}
],
"globally_reachable": false,
"network": null,
"cidr_matches": [
{
"cidr": "127.0.0.0/8",
"contains": true,
"via": "address"
},
{
"cidr": "10.0.0.0/8",
"contains": false,
"via": null
}
],
"notes": [
"\"0x7f.1\" is a non-standard IPv4 spelling. inet_aton(3), browsers, curl and other WHATWG URL parsers read it as 127.0.0.1… (110 more characters)"
]
}Same call as an MCP tools/call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "ip_inspect",
"arguments": {
"address": "0x7f.1",
"cidrs": [
"127.0.0.0/8",
"10.0.0.0/8"
]
}
}
}NAT64 address with a network prefix
curl 'https://util.caruana.me/api/ip/inspect?address=64%3Aff9b%3A%3Ac000%3A221%2F96'Response 200
{
"version": 6,
"input": "64:ff9b::c000:221/96",
"canonical": "64:ff9b::192.0.2.33",
"expanded": "0064:ff9b:0000:0000:0000:0000:c000:0221",
"zone": null,
"integer": "524413980667603649783483184533471777",
"embedded_ipv4": "192.0.2.33",
"embedding": "nat64",
"ranges": [
{
"cidr": "64:ff9b::/96",
"name": "IPv4-IPv6 Translation",
"rfc": "RFC 6052",
"registry": "ipv6-special-purpose",
"source": true,
"destination": true,
"forwardable": true,
"globally_reachable": true,
"reserved_by_protocol": false,
"via": "address"
},
{
"cidr": "192.0.2.0/24",
"name": "Documentation (TEST-NET-1)",
"rfc": "RFC 5737",
"registry": "ipv4-special-purpose",
"source": false,
"destination": false,
"forwardable": false,
"globally_reachable": false,
"reserved_by_protocol": false,
"via": "embedded_ipv4"
}
],
"globally_reachable": false,
"network": {
"cidr": "64:ff9b::/96",
"prefix": 96,
"first": "64:ff9b::",
"last": "64:ff9b::ffff:ffff",
"size": "4294967296",
"netmask": null
},
"cidr_matches": [],
"notes": [
"Host bits are set: the network is 64:ff9b::/96."
]
}Same call as an MCP tools/call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "ip_inspect",
"arguments": {
"address": "64:ff9b::c000:221/96"
}
}
}Teredo address
curl 'https://util.caruana.me/api/ip/inspect?address=2001%3A0%3A4136%3Ae378%3A8000%3A63bf%3A3fff%3Afdd2'Response 200
{
"version": 6,
"input": "2001:0:4136:e378:8000:63bf:3fff:fdd2",
"canonical": "2001:0:4136:e378:8000:63bf:3fff:fdd2",
"expanded": "2001:0000:4136:e378:8000:63bf:3fff:fdd2",
"zone": null,
"integer": "42540488182158724593221357832373272018",
"embedded_ipv4": "192.0.2.45",
"embedding": "teredo",
"ranges": [
{
"cidr": "2001::/23",
"name": "IETF Protocol Assignments",
"rfc": "RFC 2928",
"registry": "ipv6-special-purpose",
"source": false,
"destination": false,
"forwardable": false,
"globally_reachable": false,
"reserved_by_protocol": false,
"via": "address"
},
{
"cidr": "2001::/32",
"name": "TEREDO",
"rfc": "RFC 4380, RFC 8190",
"registry": "ipv6-special-purpose",
"source": true,
"destination": true,
"forwardable": true,
"globally_reachable": null,
"reserved_by_protocol": false,
"via": "address"
},
{
"cidr": "192.0.2.0/24",
"name": "Documentation (TEST-NET-1)",
"rfc": "RFC 5737",
"registry": "ipv4-special-purpose",
"source": false,
"destination": false,
"forwardable": false,
"globally_reachable": false,
"reserved_by_protocol": false,
"via": "embedded_ipv4"
}
],
"globally_reachable": false,
"network": null,
"cidr_matches": [],
"notes": [
"Teredo (RFC 4380): server 65.54.227.120, client port 40000; the client's IPv4 address is stored inverted in the last 32 bits."
]
}Same call as an MCP tools/call
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "ip_inspect",
"arguments": {
"address": "2001:0:4136:e378:8000:63bf:3fff:fdd2"
}
}
}