公开 JSON API

IPKit HTTP API

查询当前连接的公网 IP,或任意 IPv4/IPv6,返回稳定的 ipkit.v1 JSON。基址 https://ipkit.dev。无需 API 密钥。

IPKit 运行在 Cloudflare 边缘。网页与 JSON API 共用同一套查询流程。本文按实际代码说明端点、内容协商、完整 ipkit.v1 字段、错误、CORS、缓存和速率限制。没有对外 SLA,请合理使用。

基址与端点

查询地址均在 https://ipkit.dev。路径区分大小写。公开 API 不含尾斜杠。路径里的 IPv6 在部分 shell 中需要加引号或百分号编码。

GET, HEAD /
当前连接的公网 IP。浏览器(Accept: text/html)得到首页 HTML;curl 和多数脚本得到 JSON。可用 ?format=json 或 /api/my-ip 强制 JSON。
GET, HEAD /{ip}
查询指定公网 IPv4 或 IPv6,始终返回 JSON(浏览器访问也是)。例如 /8.8.8.8。语法非法返回 400 INVALID_IP;私网/保留地址返回 400 RESERVED_IP。需要 Worker 密钥 IPREGISTRY_API_KEY,否则 503 LOOKUP_UNAVAILABLE。
GET, HEAD /api/my-ip
当前公网 IP,始终 JSON。网站自身的 fetch 使用此 URL。
GET, HEAD /api/ip/{ip}
查询指定公网地址,始终 JSON。校验与服务商要求与 /{ip} 相同。构造路径时请编码地址(尤其是 IPv6),例如 /api/ip/8.8.8.8。
OPTIONS /, /{ip}, /api/my-ip, /api/ip/{ip}
所有公开 JSON 查询 URL 的 CORS 预检。204,Access-Control-Allow-Origin: *,允许 GET、HEAD、OPTIONS,Max-Age 86400。

查询路由接受 GET、HEAD 和 OPTIONS。HEAD 返回相同状态与响应头、空 body。POST 等不是查询 API 的一部分。调用方无需密钥;任意地址查询仍需要运营方配置 Worker 密钥 IPREGISTRY_API_KEY。

没有纯文本接口,也没有只返回单个字段的端点。请从 ipkit.v1 对象读取 ip(例如 jq -r .ip)。查询私网、回环、链路本地、CGNAT、组播、未指定、文档网段及其他保留 IPv4/IPv6(含 ::ffff:10.0.0.1 这类 IPv4 映射地址)会返回 400 RESERVED_IP。检测当前连接的 IP 不受该限制。

内容协商

只有站点根路径 / 会在 HTML 与 JSON 之间协商。/{ip} 和 /api/* 始终返回 JSON。format 查询参数只在 / 上生效。

?format=json
返回 JSON ipkit.v1,即使 Accept 含 text/html。
?format=html
返回 HTML 首页,即使客户端本会拿到 JSON(例如 curl)。
Accept 含 text/html
HTML 首页。常见浏览器会带上该头。
HTML 爬虫 User-Agent
Googlebot、Bingbot、百度、搜狗、360Spider 在 / 上拿到 HTML,以便索引页面而不是 JSON。
其他情况
JSON。curl 默认 Accept 为 */*,fetch() 默认也是 */*,因此不额外加头即可从 / 拿到 JSON。

ipkit.v1 响应结构

成功响应的 Content-Type 为 application/json; charset=utf-8,两空格缩进,末尾换行。字段名保持英文。null 表示没有该值。位置是网段估计,不是门牌或身份。

情报服务商成功时 source 为 "ipregistry"。仅用 Cloudflare 请求元数据组装(当前 IP 在服务商缺失或失败时)则为 "cloudflare"。Cloudflare 元数据只描述当前连接客户端,绝不会拿来当作另一个目标 IP 的归属地。该来源下 location.countryCode 来自 request.cf.country 或 CF-IPCountry,location.country 在能映射名称时填写。部分网络字段为 null,安全标志为 null,security.risk 为 "unknown"。未配置 IPREGISTRY_API_KEY 时,任意地址查询返回 503,而不会用调用方国家去猜目标地址。

schema
固定为 "ipkit.v1",客户端可据此识别破坏性变更。
ip
检测到的当前连接公网地址,或你请求查询的地址。
type
"IPv4" 或 "IPv6"。
source
情报服务商成功时为 "ipregistry";仅用 Cloudflare 边缘元数据组装时为 "cloudflare"。
location.continent
服务商提供时的大洲名称,否则为 null。
location.country
国家/地区名称。source=ipregistry 时来自服务商;source=cloudflare 时由 Cloudflare 的 ISO 代码(request.cf.country 或 CF-IPCountry)映射为英文名。代码缺失、XX(未知)、T1(Tor)或没有对应名称时为 null。
location.countryCode
已知时为 ISO 3166-1 alpha-2。Cloudflare 为当前连接客户端提供该代码。XX 记为 null;T1(Tor)保留为 T1。
location.region
网段级的州/省名称,或 null。
location.regionCode
地区代码(若有),或 null。
location.city
服务商有数据时的城市(网段级),或 null。
location.postalCode
邮编(若有),或 null。不是街道门牌。
location.latitude
估计纬度(数字),或 null。
location.longitude
估计经度(数字),或 null。
location.timezone
与归属地估计对应的 IANA 时区,或 null。
network.asn
自治系统,格式 "ASnnnn",或 null。
network.organization
该 ASN 的 ISP 或组织名,或 null。
network.domain
网络或公司域名(若有),或 null。
network.route
宣告前缀/路由(若有),或 null。
network.usage
服务商提供的连接/公司类型(如 hosting),或 null。
network.carrier
移动运营商名称(若有),或 null。
security.proxy
是否标记为代理;未知时为 null(source=cloudflare 时常见)。
security.vpn
是否标记为 VPN,或 null。
security.tor
是否标记为 Tor 出口,或 null。
security.relay
是否标记为隐私中继,或 null。
security.cloud
是否标记为云厂商,或 null。
security.threat
是否标记为威胁,或 null。只是线索,不是证据。
security.risk
"low"、"medium"、"high" 或 "unknown"。有服务商标志时据此推导;cloudflare 来源为 "unknown"。不是欺诈分,不能单独当封禁依据。

响应示例

8.8.8.8 的示意 ipkit.v1 对象。具体值随地址和数据源变化;客户端必须容忍 null。

{
  "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"
  }
}

错误与状态码

失败时返回 JSON { "error": string, "code": string },Cache-Control: no-store。error 为可读说明,code 为稳定机器码。

400 INVALID_IP
路径不是合法 IPv4/IPv6。error 为 "Invalid IP address."
400 RESERVED_IP
/{ip} 或 /api/ip/{ip} 的目标是私网、回环、链路本地、CGNAT、组播、未指定、文档网段或其他保留 IPv4/IPv6(含 IPv4 映射的 IPv6,如 ::ffff:10.0.0.1)。error 为 "Private or reserved IP address." 查询当前 IP 的 GET / 与 /api/my-ip 不会因此被拒绝。
429 RATE_LIMITED
当前客户端 IP 超过查询上限。error 为 "Too many lookups. Please retry in one minute." Retry-After 为 60。
502 LOOKUP_UNAVAILABLE
情报服务商失败或超时(约 6 秒)。error 为 "The IP data provider is temporarily unavailable."
503 LOOKUP_UNAVAILABLE
对 /{ip} 与 /api/ip/{ip} 通常表示服务 ipkit.dev 的 Cloudflare 账号未设置 Worker 密钥 IPREGISTRY_API_KEY(空或缺)。此时 error 为 "IP intelligence is not configured for arbitrary lookups." 这是运营配置问题,不是客户端错误。请在控制台 Workers & Pages → ipkit → Settings → Variables and Secrets 设置,或执行 wrangler secret put IPREGISTRY_API_KEY。当前 IP 的 GET / 与 /api/my-ip 仍可用 Cloudflare 元数据成功。同一状态码也会在 Ipregistry 限制 IPKit 时出现,那时 error 为 "The IP data provider is temporarily unavailable."
500 LOOKUP_UNAVAILABLE
未预期失败。error 为 "Unexpected lookup failure."

CORS

所有 JSON 查询响应(含错误)都带 Access-Control-Allow-Origin: *。OPTIONS 预检在 /、/{ip}、/api/my-ip、/api/ip/{ip} 返回 204,允许 GET、HEAD、OPTIONS,Max-Age 86400。浏览器在 / 拿到的 HTML 不是 CORS JSON 响应。

速率限制

每个连接客户端 IP 每 60 秒最多 300 次查询,包括当前 IP 与任意地址。没有对外 SLA 或可用性承诺。请合理使用;这是免费公共工具,不是签约 API。

缓存

当前 IP 的 JSON(协商后的 / 与 /api/my-ip)为 private, no-store,避免把一个客户端的地址复用给另一个。指定 IP 查询(/{ip} 与 /api/ip/{ip})为 public, max-age=300, s-maxage=3600, stale-while-revalidate=86400。错误响应为 no-store。/ 的 JSON 还按 Accept 与 CF-Connecting-IP 变化。

可复制示例

无需鉴权。仅 HTTPS。请解析 schema,并处理 null 与错误对象。

当前公网 IP(curl)

curl https://ipkit.dev

查询指定地址

curl https://ipkit.dev/8.8.8.8

路径中的 IPv6(请加引号)

curl "https://ipkit.dev/2001:4860:4860::8888"

始终返回 JSON 的当前 IP

curl https://ipkit.dev/api/my-ip

JavaScript 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 一行(脚本 / DDNS)

curl -sS https://ipkit.dev | jq -r .ip

隐私

为完成查询,IPKit 会处理被查询地址和常规请求元数据。查询当前 IP 时使用连接到 Cloudflare 的公网地址。详细查询可能发给配置的情报服务商;凭证不会暴露给浏览器。应用不会保存访客 IP 数据库。Cloudflare 与数据服务商可能按各自政策保留运维日志。HTML 页可能加载分析脚本;JSON 响应不含这些脚本。位置和安全属性都是估算,不能当作门牌、身份或滥用的确定证据。

完整隐私政策

FAQ

需要 API 密钥吗?

调用方不需要。对公开 HTTPS URL 发 GET 即可。不要抓取 HTML 当结构化数据。任意地址查询仍要求站点运营方设置 Worker 密钥 IPREGISTRY_API_KEY。

怎样只要 IP 字符串?

没有 text/plain 端点。解析 JSON:curl -sS https://ipkit.dev | jq -r .ip

为什么浏览器打开 / 是网页不是 JSON?

根路径在 Accept 含 text/html 时返回 HTML。请用 ?format=json、/api/my-ip,或不发送 HTML Accept 的客户端。

可以查私网地址吗?

不可以。/{ip} 与 /api/ip/{ip} 对私网和保留地址返回 HTTP 400、code RESERVED_IP。公网地址查询行为不变。GET / 与 /api/my-ip 仍返回当前连接客户端。

为什么 /8.8.8.8 返回 503 LOOKUP_UNAVAILABLE?

若 error 正是 "IP intelligence is not configured for arbitrary lookups.",表示服务 ipkit.dev 的 Cloudflare 账号未设置 Worker 密钥 IPREGISTRY_API_KEY。当前 IP 查询仍可用 Cloudflare 元数据。这不是客户端 CORS 或格式问题。