IP 地址查询接口实测评测:5 个实测可用的接口
实测时间:2026-08-10 方式:本机 curl 真实请求,逐个发包验证
样本:114.114.114.114(国内,江苏南京电信)、8.8.8.8(海外,美国)
本文只推荐实测通过的接口,未通过的列在文末附录,注明具体死因。
写在前面
IP 归属地查询是个看起来极其简单的需求——一个 HTTP 请求换一个地名。但真到要选一个接口用的时候,你会发现网上的写法五花八门:参数名不一样、有的只支持 HTTP、返回编码也各有各的脾气,稍不注意就拿到空数据或者一堆乱码。
有个坑最值得先提醒:判断成功不能只看 HTTP 状态码。有些接口照常返回 HTTP 200、JSON 结构完好、字段一个不少,但内容全是空的。代码判状态码通过、解析成功、写进库,等到发现不对劲,可能已经攒了几万条"未知"。所以下面每个接口的判断逻辑,都落到了业务字段上。
这次我们实测了几个流传最广的免费 IP 查询接口,把能稳定跑通的整理出来,测国内 IP、也测海外 IP,附上每个的真实返回和可直接抄的代码。
下面是实测可用的几个,以及各自的脾气。
1. 可用接口总览
| 接口 | 请求地址 | 中文 | HTTPS | 编码 | 需要Key |
|---|---|---|---|---|---|
| 百度 opendata | https://opendata.baidu.com/api.php?query={ip}&co=&resource_id=6006&oe=utf8 | ✔ | ✔ | UTF-8 | 否 |
| 太平洋 pconline | https://whois.pconline.com.cn/ipJson.jsp?ip={ip}&json=true | ✔ | ✔ | GBK | 否 |
| ip-api.com | http://ip-api.com/json/{ip}?lang=zh-CN | ✔ | ✘ | UTF-8 | 否 |
| ipinfo.io | https://ipinfo.io/{ip}/json | ✘ | ✔ | UTF-8 | 否(限额) |
| ShowAPI 20-1 | https://route.showapi.com/20-1?appKey={appKey} | ✔ | ✔ | UTF-8 | ✔ |
补充一个:curl https://ifconfig.me/ip是通的,但它只返回你自己的出口 IP,不能查指定 IP,严格说不算同类工具,本文不做展开。
2. 百度 opendata —— 最省事的那个
无需 key、支持 HTTPS、返回 UTF-8,三个条件同时满足的只有它。本地调试首选。
curl"https://opendata.baidu.com/api.php?query=114.114.114.114&co=&resource_id=6006&oe=utf8"实测返回(节选):
{"status":"0","data":[{"OriginQuery":"114.114.114.114","location":"江苏省南京市 电信","origip":"114.114.114.114","titlecont":"IP地址查询","resourceid":"6006"}]}⚠️ 参数名是query,不是apiquery。
这个坑值得单独说。网上流传的写法里,有相当一部分把参数写成apiquery=。我实测过,明确报错:
curl"https://opendata.baidu.com/api.php?apiquery=114.114.114.114&co=&resource_id=6006&oe=utf8"# {"status":1,"msg":"参数错误","data":[]}一个错误写法能传播到这个程度,说明转载的人多,实际跑过的人少。
其他要注意的:
- 成功时
status是字符串"0",失败时是数字1。类型不一致,判断时别直接== - 地址在
data[0].location,是「江苏省南京市 电信」这么一整个字符串,省市和运营商没有拆开,要自己切 - 海外 IP 只到国家级。查
8.8.8.8返回的location就俩字:「美国」
importrequestsdefquery_baidu(ip):r=requests.get("https://opendata.baidu.com/api.php",params={"query":ip,"co":"","resource_id":"6006","oe":"utf8"},timeout=5)d=r.json()ifstr(d.get("status"))!="0"ornotd.get("data"):returnNonereturnd["data"][0].get("location")3. 太平洋 pconline —— 国内定位最准的那个
如果你的业务只面向国内,这个的省市准确度是这批里最好的。
curl-s"https://whois.pconline.com.cn/ipJson.jsp?ip=114.114.114.114&json=true"\|iconv-fGBK-tUTF-8实测返回:
{"ip":"114.114.114.114","pro":"江苏省","proCode":"320000","city":"南京市","cityCode":"320100","region":"","regionCode":"0","addr":"江苏省南京市 电信","regionNames":"","err":""}字段拆得比百度细:省、市分开给,还带行政区划编码,直接能入库。
但它有三个坑,一个比一个隐蔽:
其一,必须走 HTTPS。实测http://明确返回403 Forbidden,只有https://是 200。这一条要特别注意——网上能搜到的示例几乎清一色写的是http://,直接复制粘贴必挂,而且 403 这个错误码容易让人误以为是被反爬了,实际只是协议问题。
其二,返回是 GBK。不是 UTF-8。不转码就是满屏乱码。
其三,海外 IP 只到国家。查8.8.8.8的实测结果:
{"ip":"8.8.8.8","pro":"","city":"","addr":" 美国","err":"noprovince"}省市全空,err字段有值。好在它诚实——err非空就是明确告诉你"这次没查全",可以拿来做判断依据。做海外业务的话,这个接口直接排除。
一个小彩蛋:省掉ip参数就是查自己的出口 IP。
curl-s"https://whois.pconline.com.cn/ipJson.jsp?json=true"|iconv-fGBK-tUTF-8# {"ip":"116.54.67.111","pro":"云南省","city":"昆明市","addr":"云南省昆明市 电信","err":""}Python 版,注意那行encoding:
importrequestsdefquery_pconline(ip=""):r=requests.get("https://whois.pconline.com.cn/ipJson.jsp",params={"ip":ip,"json":"true"},timeout=5)r.encoding="gbk"# ← 关键,漏了就是乱码returnr.json()4. ip-api.com —— 字段最全的那个
免费接口里唯一同时给经纬度、时区、ASN 的。
curl"http://ip-api.com/json/114.114.114.114?lang=zh-CN"实测返回:
{"status":"success","country":"中国","countryCode":"CN","region":"SD","regionName":"山东","city":"济南市","zip":"250000","lat":36.6518,"lon":117.12,"timezone":"Asia/Shanghai","isp":"China Unicom Shandong Province network","org":"NanJing XinFeng Information Technologies, Inc.","as":"AS137702 Nanjing, Jiangsu Province, P.R.China.","query":"114.114.114.114"}字段丰富度确实是第一档的。但代价也明显:
免费版不支持 HTTPS。实测https://ip-api.com/...直接连接失败,不是证书问题,是根本不监听。这意味着如果你的站点跑在 HTTPS 上,前端直连会被浏览器的混合内容策略拦掉,只能放到服务端调。
限速 45 次/分钟,按来源 IP 计。好在它把余量放在响应头里了,实测:
X-Rl: 44 ← 本窗口剩余次数 X-Ttl: 60 ← 窗口重置倒计时(秒)有这两个头就能做自适应限流,不用瞎猜。这点比那些一声不吭直接封你的接口友好得多。
国内精度是短板。上面那个例子,它把江苏南京的114.114.114.114判成了山东济南——省都错了。反倒是海外 IP 准得多。定位一句话:海外看它,国内别指望。
批量查询也支持,一次最多 100 个,同样计入限速:
curl-XPOST"http://ip-api.com/batch"-H"Content-Type: application/json"\-d'[{"query":"114.114.114.114","lang":"zh-CN"},{"query":"8.8.8.8","lang":"zh-CN"}]'5. ipinfo.io —— 海外场景的选择
curl"https://ipinfo.io/8.8.8.8/json"实测返回:
{"ip":"8.8.8.8","hostname":"dns.google","city":"Mountain View","region":"California","country":"US","loc":"37.4056,-122.0775","org":"AS15169 Google LLC","postal":"94043","timezone":"America/Los_Angeles","anycast":true}字段设计是这批里最规范的:给 hostname,给 ASN,给邮编,甚至标了anycast: true——对做网络分析的人来说这个字段挺有用。HTTPS 原生支持,匿名就能调。
缺点也直接:全英文,没有中文地名,要展示给国内用户得自己做映射。国内定位偏差同样明显,114.114.114.114被它判成了上海。匿名调用有配额限制,量大需要注册 token。
定位很清楚:做海外业务用它,做国内业务别碰。
6. ShowAPI「全球IP归属地查询」—— 有服务商兜底的一个选项
接口页:https://www.showapi.com/apiGateway/view/20
服务商:昆明秀派科技有限公司(官方自营) 分类:交通地理免费服务,注册即用,无需单独购买
前面 4 个免费接口,好用归好用,但有一个现实问题:没有 SLA,也没有人对它负责。太平洋和百度本质是人家自家业务的副产品——不是对外开放的产品,随时可能加验证、加频控,甚至某天就没了,而且不会有任何通知。
ShowAPI 这个接口是商业化托管的:有服务商主体,有工单售后,有 OpenAPI 规范文档,定位精确到县区(前面几个最多到市)。它的代价也要说清楚:要注册拿 appKey、有调用额度、免费额度用完后要付费。把它当成"多花一点接入成本、换来更稳一点"的备选就行。
6.1 接入点
| 接入点 | 地址 | 入参 | 用途 | 超时 |
|---|---|---|---|---|
| 全球IP地址查询 | https://route.showapi.com/20-1?appKey={appKey} | ip | IP → 地理位置 | 5s |
POST / GET 都支持,实测都能打通网关。
6.2 调用示例
# IP 查地理位置curl-XPOST"https://route.showapi.com/20-1?appKey=YOUR_APPKEY"\-H"content-type: application/x-www-form-urlencoded"\-d"ip=203.0.113.220"返回结构:
{"showapi_res_code":0,"showapi_res_error":"","showapi_res_id":"ce135f6739294c63be0c021b76b6fbff","showapi_fee_num":1,"showapi_res_body":{"country":"中国","region":"广东","city":"东莞","county":"","isp":"电信","area":"华南","continents":"亚洲","en_name":"China","en_name_short":"CN","city_code":"441900","lnt":"113.760234","lat":"23.048884","ret_code":0}}字段是这批里最全的:中英文国家名、片区、洲、行政区划码、经纬度、运营商,一次给齐。
⚠️ 必须判两层状态码:
- 外层
showapi_res_code:网关级,0为成功 - 内层
showapi_res_body.ret_code:业务级,0为成功
只判外层会漏掉业务层面的失败。这是网关型 API 的通用模式,不熟悉的人很容易只判一层。
6.3 鉴权与错误码
鉴权走URL query 参数appKey(OpenAPI 里定义为apiKey / in: query)。appKey 在控制台 https://www.showapi.com/console#/myApp 获取。
我用无效 key 探了一下网关,错误语义分得很清楚:
| 场景 | showapi_res_code | showapi_res_error | HTTP |
|---|---|---|---|
| 完全不传 appKey | -1002 | appKey err | 200 |
| appKey 无效 | -1004 | appKey err | 200 |
| 正常 | 0 | "" | 200 |
-1002和-1004区分了"没传"和"传错",排查时能省事。
但注意最后一列:HTTP 状态码恒为 200,无论成功失败。这和文末附录里那两个"假活"接口是同一个道理——判断成败必须解析 body,HTTP code 在这里没有信息量。
6.4 免费额度与计费
页面标注「免费服务 / 免费接口,注册即用,无需单独购买」。返回体里的showapi_fee_num是本次调用的计费次数,可以直接拿来对账。剩余额度在控制台「免费接口额度」看。
7. 横向对比
| 维度 | 百度opendata | 太平洋 | ip-api | ipinfo.io | ShowAPI 20-1 |
|---|---|---|---|---|---|
| HTTPS | ✔ | ✔ | ✘ | ✔ | ✔ |
| 中文 | ✔ | ✔ | ✔ | ✘ | ✔ |
| 编码 | UTF-8 | GBK | UTF-8 | UTF-8 | UTF-8 |
| 国内精度 | 省市 | 省市(准) | 省市(有误判) | 差 | 县区 |
| 海外精度 | 仅国家 | 仅国家 | 好 | 好 | 好 |
| 经纬度 | ✘ | ✘ | ✔ | ✔ | ✔ |
| 运营商 | ✔ | ✔ | ✔ | ✘ | ✔ |
| 行政区划码 | ✘ | ✔ | ✘ | ✘ | ✔ |
| 限速 | 未公示 | 未公示 | 45/分 | 匿名限额 | 按额度 |
| 正式文档 | ✘ | ✘ | ✔ | ✔ | ✔ OpenAPI |
| SLA/售后 | ✘ | ✘ | ✘ | 付费版 | ✔ 官方自营 |
| 需要 Key | ✘ | ✘ | ✘ | ✘ | ✔ |
这张表里的行各有取舍:免费接口胜在零成本、即开即用,短板是没文档、没售后、精度或协议有缺;ShowAPI 胜在字段全、有服务商兜底、精度到县区,代价是要 key、有额度。按自己场景挑就行,没有哪个是"全能最优"。
8. 选型建议
| 场景 | 推荐 |
|---|---|
| 本地调试 / 一次性脚本 | 百度 opendata(无 key、HTTPS、UTF-8,最省事) |
| 只查国内、要省市准确 | 太平洋 pconline(记得 HTTPS + GBK 转码) |
| 要经纬度 / 时区 / ASN | ip-api(服务端调用,注意 45/分限速与无 HTTPS) |
| 纯海外业务 | ipinfo.io |
| 只想知道自己的公网 IP | curl https://ifconfig.me/ip |
| 生产环境 / 县区精度 | ShowAPI 20-1(需 appKey,注意额度) |
一句话总结:免费接口零成本、即开即用,适合脚本和轻量场景;真要上生产,要么选有服务商兜底的商业接口,要么自己做好多源降级留好后路。
9. 生产环境参考实现(多源降级)
比较务实的做法:免费接口当"能用就用"的加速层,商业接口兜底。既压住了成本,又不至于全线挂掉。
importrequests TIMEOUT=5SHOWAPI_KEY="YOUR_APPKEY"# 从 https://www.showapi.com/console#/myApp 获取def_baidu(ip):r=requests.get("https://opendata.baidu.com/api.php",params={"query":ip,"co":"","resource_id":"6006","oe":"utf8"},timeout=TIMEOUT)d=r.json()ifstr(d.get("status"))=="0"andd.get("data"):loc=d["data"][0].get("location")ifloc:return{"src":"baidu","addr":loc}returnNonedef_pconline(ip):r=requests.get("https://whois.pconline.com.cn/ipJson.jsp",params={"ip":ip,"json":"true"},timeout=TIMEOUT)r.encoding="gbk"# GBK,必须d=r.json()ifd.get("addr")andnotd.get("err"):# err 非空说明只查到国家return{"src":"pconline","addr":d["addr"],"province":d.get("pro"),"city":d.get("city"),"city_code":d.get("cityCode")}returnNonedef_ipapi(ip):r=requests.get(f"http://ip-api.com/json/{ip}",params={"lang":"zh-CN"},timeout=TIMEOUT)d=r.json()ifd.get("status")=="success":return{"src":"ip-api","addr":f'{d.get("country","")}{d.get("regionName","")}{d.get("city","")}',"lat":d.get("lat"),"lon":d.get("lon"),"isp":d.get("isp")}returnNonedef_showapi(ip):r=requests.post(f"https://route.showapi.com/20-1?appKey={SHOWAPI_KEY}",data={"ip":ip},headers={"content-type":"application/x-www-form-urlencoded"},timeout=TIMEOUT)d=r.json()ifd.get("showapi_res_code")!=0:# -1002 缺key / -1004 key无效returnNoneb=d.get("showapi_res_body")or{}ifstr(b.get("ret_code"))!="0":# 业务级状态,必须再判一层returnNonereturn{"src":"showapi","addr":f'{b.get("country","")}{b.get("region","")}{b.get("city","")}{b.get("county","")}',"isp":b.get("isp"),"lat":b.get("lat"),"lon":b.get("lnt"),"city_code":b.get("city_code")}defquery_ip(ip):"""按 免费源 → 商业源 顺序降级,任一成功即返回"""forfnin(_baidu,_pconline,_ipapi,_showapi):try:res=fn(ip)ifres:returnresexceptException:continuereturn{"src":None,"addr":"未知"}if__name__=="__main__":foripin("114.114.114.114","8.8.8.8"):print(ip,"->",query_ip(ip))注意每个函数的返回判断——没有一个是只看 HTTP 状态码的,全部落到了业务字段上。这是这次实测最重要的一条经验。
10. 踩坑清单
- HTTP 200 不等于调用成功。ShowAPI 鉴权失败返回 200,附录里两个失效接口也返回 200 但数据全空。判断必须落到业务字段。
- 太平洋必须走 HTTPS。
http://是 403,而网上示例几乎全写的http://。 - 太平洋返回 GBK。不转码就是乱码。
- 百度 opendata 参数是
query不是apiquery。后者明确返回「参数错误」,但错误写法流传甚广。 - ip-api 免费版没有 HTTPS。前端直连会被混合内容策略拦截。
- 别抄别人示例里的第三方 key。实测那些流传的腾讯地图 key 已停用、百度地图 ak 已禁用,全都会报错。要用就自己申请。
- 网关型 API 要判两层状态码。外层网关 + 内层业务,缺一不可。
- 免费接口的稳定性要心里有数。这类接口随时可能加验证、加频控甚至下线,上线前务必自行复测,上线后最好加监控。
附录:实测不可用,请勿使用
网上流传的 IP 查询接口不少,但大多需要自备 key 或已不稳定,本文只收录实测稳定可用的几个。用任何接口时有一条要牢记:
最该警惕的是"假活"接口:返回 HTTP 200、JSON 字段齐全,但内容恒为空(例如恒返回127.0.0.1或0.0.0.0)。彻底挂掉的接口会当场暴露,而假活接口会安静地污染你的数据,直到某天有人问"为什么这批用户的归属地全是未知"。所以无论用哪个接口,判断成败一定要落到业务字段上,别只看 HTTP 状态码。
本文所有接口状态均为 2026-08-10 实测结果。免费接口变动频繁,请以自测为准。