Do I need an API key?
Callers do not. Send GET to the public HTTPS URLs. Do not scrape the HTML pages for structured data. Arbitrary lookups still require the site operator to set the Worker secret IPREGISTRY_API_KEY.
Look up the public IP of this connection, or any IPv4 or IPv6 address, as a stable ipkit.v1 JSON record. Base URL https://ipkit.dev. No API key.
IPKit runs on Cloudflare’s edge. The HTML site and the JSON API share the same lookup pipeline. This page documents the HTTP API as the code implements it: endpoints, content negotiation, the full ipkit.v1 schema, errors, CORS, caching, and rate limits. There is no published SLA; use the service reasonably.
All lookup URLs are on https://ipkit.dev. Paths are case-sensitive. Trailing slashes are not part of the public API. IPv6 literals in a path may need quotes or percent-encoding in some shells.
Lookup routes accept GET, HEAD, and OPTIONS. HEAD returns the same status and headers with an empty body. POST and other methods are not part of the lookup API. Clients do not send an API key; arbitrary lookups still need the operator-configured IPREGISTRY_API_KEY Worker secret.
There is no plaintext-only route and no endpoint that returns a single field. Read ip from the ipkit.v1 object (for example jq -r .ip). Lookups of private, loopback, link-local, CGNAT, multicast, unspecified, documentation, and other reserved IPv4/IPv6 ranges (including IPv4-mapped IPv6 such as ::ffff:10.0.0.1) return 400 RESERVED_IP. Current-IP detection of this connection is not subject to that check.
Only the site root (/) negotiates HTML vs JSON. /{ip} and /api/* always return JSON. Query parameter format is read only on /.
Successful lookups return Content-Type application/json; charset=utf-8, pretty-printed with two-space indent and a trailing newline. Field names stay in English. null means that value was not supplied. Locations are network estimates, not a street address or identity.
source is "ipregistry" when the configured intelligence provider succeeded. It is "cloudflare" when the record is built from Cloudflare request metadata (used for the current IP if the provider is missing or fails). Cloudflare metadata describes the connecting client only; it is never used as geolocation for a different target IP. Cloudflare-sourced records fill location.countryCode from request.cf.country or CF-IPCountry and location.country from that code when a name is known. Several network fields stay null, security flags are null, and security.risk is "unknown". If IPREGISTRY_API_KEY is unset, arbitrary lookups return 503 instead of guessing the caller’s country.
Illustrative ipkit.v1 object for 8.8.8.8. Values change with the address and data source; clients must tolerate nulls.
{
"schema": "ipkit.v1",
"ip": "8.8.8.8",
"type": "IPv4",
"source": "ipregistry",
"location": {
"continent": "North America",
"country": "United States",
"countryCode": "US",
"region": "California",
"regionCode": "CA",
"city": "Mountain View",
"postalCode": null,
"latitude": 37.386,
"longitude": -122.0838,
"timezone": "America/Los_Angeles"
},
"network": {
"asn": "AS15169",
"organization": "Google LLC",
"domain": "google.com",
"route": "8.8.8.0/24",
"usage": "hosting",
"carrier": null
},
"security": {
"proxy": false,
"vpn": false,
"tor": false,
"relay": false,
"cloud": true,
"threat": false,
"risk": "medium"
}
}Failures return JSON { "error": string, "code": string } with Cache-Control: no-store. error is a human-readable message; code is a stable machine token.
Every JSON lookup response, including errors, includes Access-Control-Allow-Origin: *. OPTIONS preflight returns 204 on /, /{ip}, /api/my-ip, and /api/ip/{ip} with Allow-Methods GET, HEAD, OPTIONS and Access-Control-Max-Age 86400. HTML from / (browsers) is not a CORS JSON response.
Lookups are limited to 300 requests per 60 seconds per connecting client IP. The limit applies to current-IP and arbitrary lookups. There is no published SLA or uptime promise. Please keep usage reasonable; this is a free public tool, not a contracted API.
Current-IP JSON (negotiated / and /api/my-ip) is Cache-Control: private, no-store so one client’s address is not reused for another. Specific-IP lookups (/{ip} and /api/ip/{ip}) are Cache-Control: public, max-age=300, s-maxage=3600, stale-while-revalidate=86400. Error responses are no-store. / JSON also varies on Accept and CF-Connecting-IP.
No authentication. HTTPS only. Parse schema and handle nulls and error objects.
Current public IP (curl)
curl https://ipkit.devLook up an address
curl https://ipkit.dev/8.8.8.8IPv6 in the path (quote colons)
curl "https://ipkit.dev/2001:4860:4860::8888"Always-JSON current IP
curl https://ipkit.dev/api/my-ipJavaScript fetch
const response = await fetch('https://ipkit.dev', {
headers: { Accept: 'application/json' },
});
const data = await response.json();
console.log(data.ip, data.location.countryCode);Python
import json, urllib.request
with urllib.request.urlopen('https://ipkit.dev') as response:
data = json.load(response)
print(data['ip'], data['location']['countryCode'])Shell one-liner (scripts / DDNS)
curl -sS https://ipkit.dev | jq -r .ipTo answer a lookup, IPKit processes the requested address and standard request metadata. Current-IP lookups use the public address that connected to Cloudflare. Detailed lookups may be sent to the configured intelligence provider; provider credentials are never exposed to the browser. IPKit does not keep an application database of visitor IPs. Cloudflare and the data provider may retain operational logs under their own policies. HTML pages may load analytics; JSON responses do not include those scripts. IP location and security attributes are estimates, not a street address, identity, or definitive evidence of abuse.
Callers do not. Send GET to the public HTTPS URLs. Do not scrape the HTML pages for structured data. Arbitrary lookups still require the site operator to set the Worker secret IPREGISTRY_API_KEY.
There is no text/plain endpoint. Parse JSON: curl -sS https://ipkit.dev | jq -r .ip
The root URL serves HTML when Accept includes text/html. Use ?format=json, /api/my-ip, or a client that does not send HTML Accept.
No. /{ip} and /api/ip/{ip} reject private and reserved ranges with HTTP 400 and code RESERVED_IP. Public addresses are looked up as before. GET / and /api/my-ip still return the connecting client.
That exact error ("IP intelligence is not configured for arbitrary lookups.") means the Worker secret IPREGISTRY_API_KEY is not set on the Cloudflare account serving ipkit.dev. Current-IP lookups still work from Cloudflare metadata. It is not a client-side CORS or format problem.