# IP 查询 API — 完整参考（AI 可读单文件版）

Source: https://ip.denys.cn/docs
更新时间随站点发布。

## 简介

IP 查询 API（ip.denys.cn）提供 IP 归属地查询 API：支持 IPv4 与 IPv6，毫秒级返回国家、省份、城市、运营商；按次计费、仅查询命中（found=true）计费，次数长期有效；注册并完成邮箱验证即送 1,000 次免费额度。

## 接口

- `GET https://ip.denys.cn/v1/query?ip=223.5.5.5` — 查询单个 IP（IPv4/IPv6 均可）；省略 ip 参数时查询调用方 IP。
- `POST https://ip.denys.cn/v1/query` — 批量查询，请求体 {"ips":["223.5.5.5","119.29.29.29","2400:3200::1"]}，IPv4/IPv6 可混排，单次最多 100 个（超出返回 413 too_many_ips），响应为 {"results":[...]}，顺序与请求一致。

## 鉴权

所有请求携带请求头 `X-API-Key: ipk_your_key`（在 https://ip.denys.cn/console 创建，每账号最多 50 个）。
也支持 ?key= 查询参数，但会进入访问日志，不建议使用。

## 返回格式

响应均为 application/json。GET 返回单个对象，POST 返回 {"results":[...]}，每项结构相同：

| 字段 | 类型 | 说明 |
|---|---|---|
| ip | string | 查询的 IP（IPv4/IPv6 均原样回显） |
| found | boolean | 是否命中；false 表示未收录（内网/保留地址等），不计费 |
| country | string | 国家或地区 |
| province | string | 省份/州，可能为空 |
| city | string | 城市，可能为空 |
| isp | string | 运营商/网络归属，可能为空 |
| country_code | string | ISO 3166-1 alpha-2 国家码，如 CN、US；港澳台统一返回 CN |

IPv4 响应示例：

```json
{
  "ip": "223.5.5.5",
  "found": true,
  "country": "中国",
  "province": "浙江省",
  "city": "杭州市",
  "isp": "阿里",
  "country_code": "CN"
}
```

IPv6 响应示例（结构完全相同）：

```json
{
  "ip": "2400:3200::1",
  "found": true,
  "country": "中国",
  "province": "浙江省",
  "city": "杭州市",
  "isp": "阿里",
  "country_code": "CN"
}
```

港澳台说明：香港、澳门、台湾的 IP 统一返回 country=中国、country_code=CN，以 province 区分：香港特别行政区、澳门特别行政区、台湾省（city 为空）。判断是否中国大陆，用 province 不属于以上三个值即可。

## 计费规则

- 仅查询命中（found=true）计费；未命中、IP 格式非法、服务错误均不计费。
- GET 每次最多计 1 次；POST 批量按响应中 found=true 的个数计费，余额不足扣到 0。
- 每次成功（200）响应带 X-Quota-Remaining 头，为本次扣减后的剩余次数。
- 次数长期有效，不设有效期；不自动扣费，用尽后接口返回 402。

## 定价

| 套餐 | 价格 | 次数 | 单价 |
|---|---|---|---|
| 入门 | ¥5 | 5,000 次 | 0.0010 元/次 |
| 标准 | ¥20 | 30,000 次 | 0.0007 元/次 |
| 专业 | ¥50 | 100,000 次 | 0.0005 元/次 |
| 旗舰 | ¥100 | 250,000 次 | 0.0004 元/次 |

## 限流与错误码

每个 API Key 约 20 次/秒（429）。错误响应均为 JSON {"error","message"}：

| HTTP 状态 | error | 说明 |
|---|---|---|
| 401 | unauthorized | 缺少或无效的 API Key |
| 402 | quota_exhausted | 次数已用完，请充值 |
| 403 | key_disabled | API Key 已被禁用 |
| 404 | not_found | 路径不存在，仅开放 /v1/query |
| 405 | method_not_allowed | 仅支持 GET/POST 查询 |
| 429 | rate_limited | 请求过于频繁，请降低调用频率 |
| 502 | upstream_error | 服务暂时不可用，请稍后重试 |

## 调用示例

**curl**

```
# 单个查询
curl 'https://ip.denys.cn/v1/query?ip=223.5.5.5' -H 'X-API-Key: ipk_your_key'

# IPv6 查询
curl 'https://ip.denys.cn/v1/query?ip=2400:3200::1' -H 'X-API-Key: ipk_your_key'

# 批量查询
curl -X POST 'https://ip.denys.cn/v1/query' \
  -H 'X-API-Key: ipk_your_key' \
  -H 'Content-Type: application/json' \
  -d '{"ips":["223.5.5.5","119.29.29.29"]}'
```

**JavaScript**

```
const res = await fetch('https://ip.denys.cn/v1/query?ip=223.5.5.5', {
  headers: { 'X-API-Key': 'ipk_your_key' },
})
const data = await res.json()
console.log('剩余次数:', res.headers.get('X-Quota-Remaining'))
console.log(data)
```

**Python**

```
import requests

res = requests.get(
    "https://ip.denys.cn/v1/query",
    params={"ip": "223.5.5.5"},
    headers={"X-API-Key": "ipk_your_key"},
)
print(res.json())
print("剩余次数:", res.headers.get("X-Quota-Remaining"))
```

**PHP**

```
<?php
$ch = curl_init('https://ip.denys.cn/v1/query?ip=223.5.5.5');
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER     => ['X-API-Key: ipk_your_key'],
    CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $data['country'], ' ', $data['province'], ' ', $data['city'], PHP_EOL;
```

**Go**

```
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	req, _ := http.NewRequest(http.MethodGet, "https://ip.denys.cn/v1/query?ip=223.5.5.5", nil)
	req.Header.Set("X-API-Key", "ipk_your_key")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	var data map[string]any
	json.NewDecoder(resp.Body).Decode(&data)
	fmt.Println(data)
}
```

**Rust**

```
use reqwest::blocking::Client;

fn main() -> Result<(), Box<dyn std::error::Error>> {
	let text = Client::new()
		.get("https://ip.denys.cn/v1/query?ip=223.5.5.5")
		.header("X-API-Key", "ipk_your_key")
		.send()?
		.text()?;
	println!("{text}");
	Ok(())
}
```

## MCP（AI Agent 接入）

本服务提供无状态 MCP 远程端点（Streamable HTTP）：`POST https://ip.denys.cn/mcp`，JSON-RPC 2.0。
鉴权与 REST 相同（X-API-Key 或 Authorization: Bearer 头），工具调用按次计费、仅命中计费。

工具：

| 名称 | 参数 | 说明 |
|---|---|---|
| ip_query | { ip?: string } | 查询单个 IP（IPv4/IPv6）；省略 ip 时查调用方出口 IP |
| ip_batch | { ips: string[] } | 批量查询，最多 100 个，按命中计费 |
| ip_quota | {} | 查询当前 Key 剩余次数（不计费） |

Claude Code 等 CLI 工具接入：`claude mcp add --transport http ip-denys https://ip.denys.cn/mcp --header "X-API-Key: ipk_xxx"`；
Claude Desktop / Cursor 等客户端在 mcpServers 配置 { "url": "https://ip.denys.cn/mcp", "headers": { "X-API-Key": "ipk_xxx" } }。

## 常见问题

### 多少钱一次？

套餐单价低至 0.0004 元/次（1 元最多可查 2,500 次）：入门 ¥5/5,000 次约 0.001 元/次，旗舰 ¥100/250,000 次为 0.0004 元/次。在同类 IP 查询 API 中属于便宜档，且仅查询命中计费，未命中与出错都不扣次数。

### 和免费接口、大厂云 API 相比有什么优势？

免费 IP 查询接口通常限流严格、数据更新和稳定性没有保障，随时可能停服或转向收费；大厂云 API 功能全但按量计费单价高。我们只做 IP 归属地查询这一件事，所以能做得更便宜：1 元最多可查 2,500 次，且仅查询命中才计费。

### 注册后有免费额度吗？

有。注册并完成邮箱验证后自动赠送 1,000 次查询额度，可以先验证数据质量再决定是否付费。

### 哪些查询会计费？

只在查询成功且有结果（found=true）时计 1 次。IP 格式非法、未收录地址（如内网地址）以及查询失败都不计费；每次成功响应的 X-Quota-Remaining 头会实时返回剩余次数。

### 次数用完会怎样？会自动扣费吗？

不会自动扣费。次数用尽后接口返回 402，充值后立即可用；所有次数长期有效，不设过期时间。

### 支持查询哪些信息？包含 IPv6 吗？

支持 IPv4 与 IPv6，毫秒级返回国家、省份、城市、运营商等字段；另有批量接口，一次最多查询 100 个 IP，按命中条数计费。

### 港澳台 IP 的查询结果怎么显示？

香港、澳门、台湾统一返回 country=中国、country_code=CN，以 province 区分：香港特别行政区、澳门特别行政区、台湾省，city 字段为空；需要判断是否中国大陆时，用 province 不属于以上三个值即可。

### AI 助手或编程 Agent 能直接接入吗？

可以。本站提供 MCP 远程端点（https://ip.denys.cn/mcp）与 AI 可读的完整接口说明（https://ip.denys.cn/llms-full.txt）：在 Claude Code / Cursor 等工具里加一行 MCP 配置即可调用；Agent 调用与人工调用同规则计费（按次、仅命中计费），注册赠送的 1,000 次足够完成试用与调试。

### 有调用频率限制吗？

有，默认每个 API Key 约 20 次/秒，超出返回 429，按业务并发量正常轮询不会触发；大批量场景建议改用批量接口合并请求。

### API Key 泄露了怎么办？

在控制台可随时重置或禁用：重置后旧 Key 立即失效。每个账号最多可创建 50 个 Key，建议按开发/生产等环境分开管理。
