Skip to main content
GET /v1/ip/{addr}
Public - no auth required. Returns ASN attribution plus, if the IP matches one of our exit nodes, the region we ran it in. This is the endpoint behind tunnelbyte.dev/ip.

Example

# A Tunnelbyte exit IP:
curl https://api.tunnelbyte.dev/v1/ip/5.223.57.171
Response
{
  "ip": "5.223.57.171",
  "asn": 24940,
  "asn_org": "Hetzner Online GmbH",
  "country": "SG",
  "is_tunnelbyte_exit": true,
  "region": "sin",
  "active_since": "2026-04-12T09:00:00Z",
  "abuse_contact": "abuse@tunnelbyte.dev"
}
# A previously-Tunnelbyte exit IP, now returned to the provider:
curl https://api.tunnelbyte.dev/v1/ip/5.6.7.8
Response
{
  "ip": "5.6.7.8",
  "asn": 24940,
  "asn_org": "Hetzner Online GmbH",
  "country": "DE",
  "is_tunnelbyte_exit": false,
  "region": "fsn",
  "active_since": "2026-04-12T09:00:00Z",
  "retired_at": "2026-06-08T18:42:00Z",
  "abuse_contact": "abuse@tunnelbyte.dev",
  "note": "This IP was operated by Tunnelbyte between active_since and retired_at. Complaints outside that window should be directed to the IP's current operator."
}
# A non-Tunnelbyte IP:
curl https://api.tunnelbyte.dev/v1/ip/1.1.1.1
Response
{
  "ip": "1.1.1.1",
  "asn": 13335,
  "asn_org": "Cloudflare, Inc.",
  "country": "US",
  "is_tunnelbyte_exit": false,
  "abuse_contact": "abuse@tunnelbyte.dev"
}

Response shape

FieldTypeNotes
ipstringThe IP that was looked up (echoed back).
asnintegerAS number from Team Cymru. 0 or missing if the lookup failed.
asn_orgstringHuman-readable AS organization name.
countrystringISO 3166-1 alpha-2.
is_tunnelbyte_exitbooleantrue if the IP is currently a Tunnelbyte exit. false for retired nodes - check retired_at.
regionstringPresent when the IP is or was a Tunnelbyte exit.
active_sincestringRFC3339 timestamp. The moment we started operating this IP. Present when we know the IP.
retired_atstringRFC3339 timestamp. Present only on retired nodes. After this date the IP has been returned to the provider’s pool and any complaint should go to its new operator.
notestringPlain-language explanation, present only when retired_at is set.
abuse_contactstringAlways abuse@tunnelbyte.dev. The whole point of this endpoint.

Errors

StatusBodyWhy
400invalid IP addressNot a parseable IPv4 / IPv6.
400address is not publicly routableLoopback, private, link-local, or multicast. We don’t attribute those.
429rate-limitedToo many requests from your source IP. Burst 10 / 1 rps.

Why this exists

When a sysadmin or abuse desk sees traffic from one of our exit IPs and wants to know who to contact, this endpoint (and the tunnelbyte.dev/ip page in front of it) gives them an immediate, authoritative answer: the complaint goes to abuse@tunnelbyte.dev. See abuse policy.

Caching

  • 5-minute in-memory cache per IP in the handler.
  • 6-hour in-memory cache per /24 (IPv4) or /48 (IPv6) for the upstream ASN lookup. The ASN lookup result is shared with the rate-limit issuance code.

CORS

Access-Control-Allow-Origin: * on every response. The browser can hit this endpoint directly from any page.