IP API

Call a keyless JSON API for your own address, or for any IP, hostname, DNS record, RDAP object or blacklist you name.

Base URL

https://whatsmyip.fyi/api/v1/

Version
v1.0.0
Endpoints
14 live
Key required
no
Uptime
Measured uptime, 30 day window

Base URL and specification

https://whatsmyip.fyi/api/v1/

The OpenAPI 3.1 document is at /api/openapi.json and is the authoritative description of every response shape. Generate a client from it rather than writing types by hand.

Rate limits

Endpoint class Limit per source address Over the limit
/api/v1/ip, /ip, /json, /ip.md 1,000 per day, 60 per minute 429 with Retry-After
/api/v1/lookup, /dns-lookup, /rdap, /reverse, /blacklist-check 200 per day, 20 per minute 429, a bot challenge after 50 per day
Privacy enrichment (?full=1, ?privacy=1) 20 per day Field returns null with a reason
Keyed tier Per-key quota 429

Every response includes the remaining quota. Read it and slow down before you are refused, rather than discovering the limit by hitting it.

Handling 429 correctly

Retry-After is a number of seconds and it is not a suggestion. A client that sleeps for that long recovers. A client that retries immediately stays blocked and adds load. If you use curl --retry, it already honours the header.

Compatibility

Within /api/v1, fields may be added and never removed. Type and meaning of an existing field will not change. Adding a field is not a breaking change, so parse defensively and ignore what you do not recognise. Anything genuinely incompatible goes to /api/v2 and v1 keeps serving.

Fair use, plainly

This is free and keyless because a small request should not need a contract. The limits exist so one client cannot make it unavailable for everyone else. Two requests that would be reasonable to ask of a stranger:

  • Cache the requester address rather than fetching it in a loop. It changes when your network changes, not every second.
  • Set a timeout and a fallback. That is good practice against any free service, including this one, and /compare/ip-apis lists alternatives that make sensible fallbacks.

The caller

GET/ip

The caller address as plain text

The address and a trailing newline. Nothing else. This is the endpoint to paste into a shell script, because the body needs no parsing and cannot grow a field later.

  • Stable
  • text/plain
  • 1,000/day, 60/min
  • private, no-store

No parameters.

Request

curl -s --fail --max-time 10 https://whatsmyip.fyi/ip

Response

203.0.113.45

GET/json

The caller report as JSON

The full record for the calling address. Served from the edge with no external call unless full=1 is set. This is the short URL; /api/v1/ip is the same response behind the version guarantee.

  • Stable
  • application/json
  • 1,000/day, 60/min
  • private, no-store
  • IpReport
ParameterInRequiredWhat it does
fullquerynoSet to 1 to resolve the reverse DNS name and the privacy flags. Both cost a provider call and are metered separately at 20 per day.

Request

curl -s --fail --max-time 10 https://whatsmyip.fyi/json

Response

{
  "ip": "203.0.113.45",
  "version": 4,
  "hostname": null,
  "city": "Tallinn",
  "region": "Harjumaa",
  "region_code": "37",
  "country": "EE",
  "country_name": "Estonia",
  "continent": "EU",
  "postal": "10111",
  "loc": "59.4370,24.7536",
  "timezone": "Europe/Tallinn",
  "local_time": "2026-09-05T14:02:11+03:00",
  "isp": "Telia Eesti",
  "asn": 3249,
  "as_name": "Telia Eesti AS",
  "as_domain": "telia.ee",
  "rir": "RIPE",
  "connection_type": "residential",
  "privacy": null,
  "network": { "protocol": "HTTP/2", "tls": "TLSv1.3", "rtt_ms": 21, "edge": "TLL" },
  "user_agent": "curl/8.7.1",
  "accepts_language": null,
  "geo_accuracy": "approximate",
  "checked_at": "2026-09-05T14:02:11Z",
  "source": "whatsmyip.fyi"
}

GET/ip.md

The caller report as Markdown

A five-line Markdown document carrying its own source URL. Written for agents and language models that handle prose better than JSON, and small enough to paste into a prompt.

  • Stable
  • text/markdown
  • 1,000/day, 60/min
  • private, no-store

No parameters.

Request

curl -s --fail --max-time 10 https://whatsmyip.fyi/ip.md

Response

# Your public IP
IP: 203.0.113.45 (IPv4)
Location: Tallinn, Harjumaa, EE (approximate)
ISP: Telia Eesti (AS3249)
Source: https://whatsmyip.fyi/ip.md

GET/api/v1/ip

The caller report, versioned

Byte-for-byte the same response as /json. Use this path in anything you will not revisit for a year, because the compatibility promise is attached to the version segment rather than to the short URL.

  • Stable
  • application/json
  • 1,000/day, 60/min
  • private, no-store
  • IpReport
ParameterInRequiredWhat it does
fullquerynoSet to 1 to resolve the reverse DNS name and the privacy flags. Both cost a provider call and are metered separately at 20 per day.

Request

curl -s --fail --max-time 10 https://whatsmyip.fyi/api/v1/ip

Response

{
  "ip": "203.0.113.45",
  "version": 4,
  "hostname": null,
  "city": "Tallinn",
  "region": "Harjumaa",
  "region_code": "37",
  "country": "EE",
  "country_name": "Estonia",
  "continent": "EU",
  "postal": "10111",
  "loc": "59.4370,24.7536",
  "timezone": "Europe/Tallinn",
  "local_time": "2026-09-05T14:02:11+03:00",
  "isp": "Telia Eesti",
  "asn": 3249,
  "as_name": "Telia Eesti AS",
  "as_domain": "telia.ee",
  "rir": "RIPE",
  "connection_type": "residential",
  "privacy": null,
  "network": { "protocol": "HTTP/2", "tls": "TLSv1.3", "rtt_ms": 21, "edge": "TLL" },
  "user_agent": "curl/8.7.1",
  "accepts_language": null,
  "geo_accuracy": "approximate",
  "checked_at": "2026-09-05T14:02:11Z",
  "source": "whatsmyip.fyi"
}

GET/api/v1/headers

Echo of the request headers

The headers the edge received, in the order received, each with a one-line explanation. Cookie and Authorization are replaced with a redaction marker so that a shared debugging transcript cannot leak a credential. Edge only, no external call.

  • Stable
  • application/json
  • 1,000/day, 60/min
  • private, no-store
  • HeadersResponse

No parameters.

Request

curl -s --fail --max-time 10 https://whatsmyip.fyi/api/v1/headers

Response

{
  "headers": [
    { "name": "user-agent", "value": "curl/8.7.1", "redacted": false, "explanation": "The client software identifying itself." },
    { "name": "authorization", "value": "[redacted]", "redacted": true, "explanation": "Credentials. Never echoed back." }
  ],
  "method": "GET",
  "protocol": "HTTP/2",
  "tls": "TLSv1.3",
  "checked_at": "2026-09-05T14:02:11Z",
  "source": "whatsmyip.fyi"
}

GET/api/v1/ua

Parse a user agent string

Browser, engine, operating system and device class. Parses the caller own header, or the string given in the ua parameter. Edge only, no external call, so it is cheap enough for high-frequency debugging.

  • Stable
  • application/json
  • 1,000/day, 60/min
  • private, no-store
  • UaResponse
ParameterInRequiredWhat it does
uaquerynoA user agent string to parse instead of the caller own.

Request

curl -s --fail --max-time 10 https://whatsmyip.fyi/api/v1/ua

Response

{
  "user_agent": "curl/8.7.1",
  "browser": { "name": "curl", "version": "8.7.1" },
  "engine": { "name": null, "version": null },
  "os": { "name": null, "version": null },
  "device": "cli",
  "bot": false,
  "cli": true,
  "checked_at": "2026-09-05T14:02:11Z",
  "source": "whatsmyip.fyi"
}

Lookup

GET/api/v1/lookup/{target}

Look up any address or hostname

The IpReport shape for a target other than the caller, plus the RFC classification of the address. A hostname is resolved first and the resolved address is reported. Private, reserved and documentation ranges return the classification and an explanation rather than a geolocation guess.

  • Stable
  • application/json
  • 200/day, 20/min
  • public, max-age=300
  • LookupResponse
ParameterInRequiredWhat it does
targetpathyesAn IPv4 address, an IPv6 address or a hostname.
privacyquerynoSet to 1 to add the VPN, proxy, Tor, hosting and relay flags. Metered at 20 per day.

Request

curl -s --fail --max-time 10 https://whatsmyip.fyi/api/v1/lookup/8.8.8.8

Response

{
  "ip": "8.8.8.8",
  "version": 4,
  "hostname": "dns.google",
  "city": null,
  "country": "US",
  "country_name": "United States",
  "isp": "Google LLC",
  "asn": 15169,
  "as_name": "GOOGLE",
  "as_domain": "google.com",
  "rir": "ARIN",
  "connection_type": "hosting",
  "privacy": null,
  "geo_accuracy": "country-only",
  "classification": {
    "kind": "public",
    "rfc": null,
    "description": "Globally routable unicast address.",
    "routable": true
  },
  "checked_at": "2026-09-05T14:02:11Z",
  "source": "whatsmyip.fyi"
}

GET/api/v1/dns

DNS over two resolvers at once

Queries more than one public resolver over DoH in parallel and returns each answer separately with its TTL and the DNSSEC AD flag. Answers are not reconciled. Which resolver said what is usually the information you came for.

  • Stable
  • application/json
  • 200/day, 20/min
  • public, max-age=60
  • DnsResponse
ParameterInRequiredWhat it does
namequeryyesThe name to query. Internationalised names are converted to A-label form and both are returned.
typequerynoA, AAAA, MX, TXT, NS, SOA, CAA, SRV, PTR, DNSKEY or DS. Defaults to A.

Request

curl -s --fail --max-time 10 'https://whatsmyip.fyi/api/v1/dns?name=example.com&type=A'

Response

{
  "name": "example.com",
  "ascii_name": "example.com",
  "type": "A",
  "answers": [
    {
      "resolver": "cloudflare",
      "status": 0,
      "ad": true,
      "records": [{ "name": "example.com", "type": "A", "ttl": 300, "data": "93.184.215.14" }],
      "error": null
    },
    {
      "resolver": "google",
      "status": 0,
      "ad": true,
      "records": [{ "name": "example.com", "type": "A", "ttl": 288, "data": "93.184.215.14" }],
      "error": null
    }
  ],
  "agree": true,
  "dnssec": true,
  "checked_at": "2026-09-05T14:02:11Z",
  "source": "whatsmyip.fyi"
}

GET/api/v1/reverse/{ip}

PTR with a forward-confirmed check

The PTR names for an address, plus the forward lookup of each name back to an address. A PTR record alone proves nothing, because the owner of the address writes it. forward_confirmed is the field that carries weight.

  • Stable
  • application/json
  • 200/day, 20/min
  • public, max-age=300
  • ReverseDnsResponse
ParameterInRequiredWhat it does
ippathyesAn IPv4 or IPv6 address.

Request

curl -s --fail --max-time 10 https://whatsmyip.fyi/api/v1/reverse/8.8.8.8

Response

{
  "ip": "8.8.8.8",
  "version": 4,
  "arpa": "8.8.8.8.in-addr.arpa",
  "hostnames": ["dns.google"],
  "forward_confirmed": true,
  "forward_addresses": ["8.8.8.8", "8.8.4.4"],
  "checked_at": "2026-09-05T14:02:11Z",
  "source": "whatsmyip.fyi"
}

GET/api/v1/mx

Mail records with SPF and DMARC parsing

MX hosts in preference order with their addresses, the SPF record with its DNS-lookup count checked against the limit of ten in RFC 7208, and the DMARC record parsed into its tags.

  • Stable
  • application/json
  • 200/day, 20/min
  • public, max-age=300
  • MxResponse
ParameterInRequiredWhat it does
domainqueryyesThe domain to inspect. Not a mail address.

Request

curl -s --fail --max-time 10 'https://whatsmyip.fyi/api/v1/mx?domain=example.com'

Response

{
  "domain": "example.com",
  "ascii_domain": "example.com",
  "records": [
    { "host": "mx1.example.com", "preference": 10, "addresses": ["203.0.113.10"], "resolves": true }
  ],
  "spf": {
    "raw": "v=spf1 include:_spf.example.net -all",
    "valid": true,
    "version": "spf1",
    "terms": [
      { "qualifier": "+", "mechanism": "include", "value": "_spf.example.net", "countsTowardLimit": true },
      { "qualifier": "-", "mechanism": "all", "value": null, "countsTowardLimit": false }
    ],
    "modifiers": [],
    "lookupCount": 1,
    "exceedsLookupLimit": false,
    "all": { "qualifier": "-", "mechanism": "all", "value": null, "countsTowardLimit": false },
    "errors": []
  },
  "dmarc": {
    "raw": "v=DMARC1; p=reject; rua=mailto:dmarc@example.com",
    "valid": true,
    "version": "DMARC1",
    "policy": "reject",
    "subdomainPolicy": null,
    "percent": null,
    "aggregateReports": ["mailto:dmarc@example.com"],
    "forensicReports": [],
    "adkim": null,
    "aspf": null,
    "failureOptions": null,
    "reportInterval": null,
    "tags": { "v": "DMARC1", "p": "reject", "rua": "mailto:dmarc@example.com" },
    "errors": []
  },
  "checked_at": "2026-09-05T14:02:11Z",
  "source": "whatsmyip.fyi"
}

GET/api/v1/ssl

Decode the TLS certificate a host presents

Opens port 443, reads the certificate chain out of a TLS 1.2 handshake, and returns the subject, every subject alternative name, the issuer chain, the validity window with days remaining, the key type and size, the signature algorithm and the SHA-256 fingerprint. A plain HTTPS request runs alongside it for the status code, HSTS and Alt-Svc. A server that refuses TLS 1.2 returns a null certificate and the HTTPS reading alone, because a TLS 1.3 handshake encrypts the certificate.

  • Stable
  • application/json
  • 200/day, 20/min
  • public, max-age=300
  • SslResponse
ParameterInRequiredWhat it does
hostqueryyesThe hostname to inspect. Port 443 only, public addresses only, no address literals.

Request

curl -s --fail --max-time 10 'https://whatsmyip.fyi/api/v1/ssl?host=example.com'

Response

{
  "host": "example.com",
  "ascii_host": "example.com",
  "port": 443,
  "addresses": [{ "address": "203.0.113.10", "family": "IPv4" }],
  "connected_address": "203.0.113.10:443",
  "certificate": {
    "subject": "CN=example.com",
    "subject_common_name": "example.com",
    "issuer": "C=US, O=Example CA, CN=Example TLS ECC CA 3",
    "issuer_common_name": "Example TLS ECC CA 3",
    "issuer_organization": "Example CA",
    "serial": "0624D0AB311558780B7D5213B9631831",
    "version": 3,
    "not_before": "2026-07-29T22:10:08Z",
    "not_after": "2026-10-27T22:17:21Z",
    "days_remaining": 52,
    "expiry_band": "ok",
    "lifetime_days": 90,
    "sans": ["example.com", "*.example.com"],
    "san_ips": [],
    "key": { "algorithm": "EC", "bits": 256, "curve": "P-256" },
    "signature_algorithm": "ecdsa-with-SHA256",
    "is_certificate_authority": false,
    "self_issued": false,
    "extended_key_usage": ["serverAuth"],
    "ocsp_responders": ["http://ocsp.example-ca.net"],
    "ca_issuers": ["http://i.example-ca.net/issuer.cer"],
    "fingerprint_sha256": "61:53:A9:6F:D1:A6:AB:7F:4D:43:8F:C3:49:32:48:42:99:D0:72:9D:91:40:B3:A1:26:BB:2F:9C:07:B0:22:00"
  },
  "chain_length": 3,
  "hostname_match": { "matched": true, "matched_name": "example.com", "checked_names": ["example.com", "*.example.com"], "used_common_name": false },
  "https": { "reachable": true, "method": "HEAD", "status": 200, "location": null, "strict_transport_security": "max-age=31536000", "alt_svc": null, "server": "example", "error": null },
  "probe": { "certificate_read": true, "method": "tls12-clienthello", "negotiated_version": "TLS 1.2", "cipher_suite": "ECDHE-ECDSA-AES128-GCM-SHA256", "alert": null, "error": null, "elapsed_ms": 101 },
  "checked_at": "2026-09-05T14:02:11Z",
  "source": "whatsmyip.fyi"
}

Registry

GET/api/v1/rdap

Normalised registration data

RDAP for an address, a domain or an AS number, flattened into one stable shape with the raw registry response kept alongside. Where a registry publishes no RDAP service the query falls back to port 43 WHOIS, and the summary says which path answered.

  • Stable
  • application/json
  • 200/day, 20/min
  • public, max-age=3600
  • RdapResponse
ParameterInRequiredWhat it does
queryqueryyesAn IP address, a CIDR block, a domain name or an AS number.

Request

curl -s --fail --max-time 10 'https://whatsmyip.fyi/api/v1/rdap?query=1.1.1.1'

Response

{
  "query": "1.1.1.1",
  "kind": "ip",
  "summary": {
    "handle": "1.1.1.0 - 1.1.1.255",
    "name": "APNIC-LABS",
    "type": "ASSIGNED PORTABLE",
    "country": "AU",
    "start_address": "1.1.1.0",
    "end_address": "1.1.1.255",
    "cidr": ["1.1.1.0/24"],
    "registry": "APNIC",
    "registered": "2011-08-10T23:12:35Z",
    "updated": "2024-03-06T02:46:16Z",
    "status": ["active"],
    "abuse_email": "helpdesk@apnic.net",
    "entities": [{ "handle": "AA1412-AP", "role": "administrative", "name": "APNIC RDNS", "email": null }],
    "source": "rdap"
  },
  "raw": {},
  "cached": false,
  "checked_at": "2026-09-05T14:02:11Z",
  "source": "whatsmyip.fyi"
}

Reputation

GET/api/v1/blacklist/{ip}

DNSBL status across attributed lists

One row per curated public blocklist, with the return code the list gave and the meaning that list documents for it. Results are cached for an hour. Every list is named, because an unattributed reputation score is not a check anyone can act on.

  • Stable
  • application/json
  • 200/day, 20/min
  • public, max-age=3600
  • BlacklistResponse
ParameterInRequiredWhat it does
ippathyesAn IPv4 or IPv6 address.

Request

curl -s --fail --max-time 10 https://whatsmyip.fyi/api/v1/blacklist/203.0.113.45

Response

{
  "ip": "203.0.113.45",
  "version": 4,
  "listed_count": 0,
  "total_count": 6,
  "results": [
    {
      "list": "Spamhaus ZEN",
      "zone": "zen.spamhaus.org",
      "listed": false,
      "codes": [],
      "reason": null,
      "lookup_url": "https://check.spamhaus.org/results/?query=203.0.113.45",
      "error": null
    }
  ],
  "checked_at": "2026-09-05T14:02:11Z",
  "source": "whatsmyip.fyi"
}

Meta

GET/api/v1/status

Measured uptime and latency

The same figures the status page renders: uptime and p50 and p95 latency per target and region over the last 24 hours and the last 30 days. Poll this rather than the badge image; the badge is a picture.

  • Stable
  • application/json
  • not metered
  • public, max-age=300
  • StatusResponse

No parameters.

Request

curl -s --fail --max-time 10 https://whatsmyip.fyi/api/v1/status

Response

{
  "generated_at": "2026-09-05T14:02:11Z",
  "available": true,
  "window": { "short_hours": 24, "long_days": 30 },
  "overall": { "uptime_24h": 99.98, "uptime_30d": 99.94, "p50_ms": 34, "p95_ms": 71, "checks_24h": 864 },
  "targets": [
    {
      "target": "https://whatsmyip.fyi/json",
      "label": "/json",
      "kind": "edge",
      "regions": [
        {
          "region": "TLL",
          "uptime_24h": 99.98,
          "uptime_30d": 99.94,
          "p50_ms": 34,
          "p95_ms": 71,
          "checks_24h": 288,
          "checks_30d": 8640,
          "last_checked_at": "2026-09-05T14:00:00Z",
          "last_ok": true
        }
      ]
    }
  ],
  "source": "whatsmyip.fyi"
}

GET/api/v1/dnsleak/{session}

DNS leak session results

The resolvers that queried the authoritative server for one session token. Planned, not shipped. The route answers 404 until the authoritative responder is live, and it appears here so that the shape is settled before anything depends on it.

  • Planned
  • application/json
  • 200/day, 20/min
  • private, no-store
  • ErrorResponse
ParameterInRequiredWhat it does
sessionpathyesThe session token handed out when the test started. Sessions are discarded after ten minutes.

Request

curl -s --fail --max-time 10 https://whatsmyip.fyi/api/v1/dnsleak/a1b2c3d4

Response

{
  "error": "not_implemented",
  "message": "The DNS leak session API is not live yet. See https://whatsmyip.fyi/dns-leak-test.",
  "status": 404
}

The IpReport shape

Every requester endpoint and the lookup base return this object. It is generated from the same schema the server validates against, so this table and openapi.json cannot drift apart. Fields are added over time and never removed, so parse defensively.

FieldTypeAlways present
ipstringyes
versionnumber | numberyes
hostnamestring or nullyes
citystring or nullyes
regionstring or nullyes
region_codestring or nullyes
countrystring or nullyes
country_namestring or nullyes
continentstring or nullyes
postalstring or nullyes
locstring or nullyes
timezonestring or nullyes
local_timestring or nullyes
ispstring or nullyes
asnnumber or nullyes
as_namestring or nullyes
as_domainstring or nullyes
rir"ARIN" | "RIPE" | "APNIC" | "LACNIC" | "AFRINIC" | "IANA" | "UNKNOWN"yes
connection_type"residential" | "mobile" | "business" | "hosting" | "education" | "government" | "unknown"yes
privacyobject | nullyes
networkobjectyes
user_agentstring or nullyes
accepts_languagestring or nullyes
geo_accuracy"approximate" | "country-only"yes
checked_atstringyes
source"whatsmyip.fyi"yes

Errors, CORS and the v1 promise

One error shape

{
  "error": "rate_limited",
  "message": "Rate limit exceeded. See https://whatsmyip.fyi/api for the published limits.",
  "status": 429
}

Every failure on every endpoint uses these three fields. error is a stable machine token, message is written for a human reading a log, andstatus repeats the HTTP status so a body copied out of context still says what happened.

CORS

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, HEAD, OPTIONS
Access-Control-Allow-Headers: Accept, Content-Type

Open on every endpoint, so browser JavaScript can call it directly. Remember what that means: the address reported is the visitor's, not your server's, and the quota is then spent per visitor.

v1 never breaks. Fields may be added. An existing field will never be removed, and its type and meaning will never change. Anything genuinely incompatible becomes /api/v2 and v1 keeps serving. That promise is the reason to use the versioned path rather than the short one.

Changelog

  1. v1.0.0

    • First published version of /api/v1. The requester endpoints /ip, /json and /ip.md are unchanged and are now described here.
    • Added /api/v1/ip, /api/v1/lookup/{target}, /api/v1/dns, /api/v1/rdap, /api/v1/reverse/{ip}, /api/v1/blacklist/{ip}, /api/v1/mx, /api/v1/headers, /api/v1/ua and /api/v1/status.
    • Published the OpenAPI 3.1 document at /api/openapi.json and the uptime badge at /status/badge.svg.
    • /api/v1/dnsleak/{session} is reserved and documented as planned. It answers 404 until the authoritative responder ships.

v1.0.0 published 2026-09-05. Every future addition is dated here before it is announced anywhere else.

About this tool

A JSON API for the address of whoever calls it, and for lookups against any target you name. Endpoints cover the requester address, IP and hostname lookup, DNS across several resolvers, RDAP, reverse DNS, blacklist checks, a header echo, and user agent parsing. The free tier needs no key, OpenAPI 3.1 describes every response, and every response reports the quota you have left.

How to read the result

GET /api/v1/ip
Returns everything the site knows about your own connection, in the same shape as /json. It is served from the edge with no external call, and ?full=1 adds reverse DNS and privacy flags at the cost of a provider call.
GET /api/v1/lookup/{ip|host}
The same record for an address or hostname you choose instead of your own. A hostname is resolved first, ?privacy=1 adds reputation flags on a separate meter, and private or reserved ranges return an explanation rather than a lookup.
GET /api/v1/dns?name=&type=
Looks up a DNS record through several resolvers at once over DoH, and returns each answer with its TTL and the DNSSEC AD flag. When resolvers disagree you get both answers, because which resolver said what is usually the information you need.
GET /api/v1/rdap?query=
Domain and address registration records, normalised into a stable shape with the raw registry response included. Where a registry runs no RDAP service it falls back to port 43 WHOIS, and the response says which path was used.
GET /api/v1/reverse/{ip} and /blacklist-check/{ip}
The name attached to an address, checked forward-confirmed, and DNSBL status per list with the return code and its documented meaning. Blacklist results are cached for one hour.
GET /api/v1/headers and /api/v1/ua
Your request headers echoed back with Cookie and Authorization redacted, and a breakdown of any user agent string. Both are edge-only with no external call, so they are cheap enough for high-frequency debugging.
Rate limit headers and 429
Every response carries the quota you have left. Over the limit you get 429 with Retry-After in seconds, and a client that retries immediately stays rate limited.

Questions people ask

Do I need an API key?
Not for the free tier. Keyless access gives 1,000 requests per day for the requester-address endpoints and 200 per day for lookup-class endpoints, per source address. A keyed tier with higher quotas exists for volume, but nothing is gated behind sign-up for ordinary use.
Is CORS enabled?
Yes, Access-Control-Allow-Origin is a wildcard on /ip, /json, /ip.md, and every /api/v1 endpoint, so you can call it directly from browser JavaScript. Note that a browser call reports the visitor's address, which is usually the point, and that your quota is then consumed per visitor rather than per server.
Will the v1 API change?
Fields may be added. Existing fields will not be removed or change type or meaning within v1. A breaking change means a v2 path, and v1 keeps working. The changelog on this page records every addition with its date.
What is the difference between /json and /api/v1/ip?
Nothing in the response. /json is the short URL for humans and quick scripts, /api/v1/ip is the versioned path that carries the compatibility guarantee. Use the versioned path in anything you will not revisit for a year.
What happens if a data provider is unavailable?
The affected field returns null with a reason, and the rest of the response is served normally. We do not fail an entire response because one enrichment source timed out, and we do not serve a stale value without saying so. Provider budget guards degrade the same way at 80 percent of a daily quota.

Related

Last reviewed 2026-09-05.