手机归属地查询返回 -2、-3、-4 是什么意思?错误码逐条对照与排查
约 14 分钟 进阶手机号码归属地查询错误码排查ret_code参数校验
# 手机归属地查询返回 -2、-3、-4 是什么意思?错误码逐条对照与排查
接口:手机归属地查询(apiCode=6,接入点 6-1)|是否免费:是,注册后默认可调用,设使用档次限制|请求方式:POST / GET|返回格式:JSON|适用人群:正在排查查询失败的开发者|阅读时间:约 7 分钟|最后实测核对:2026-09-18
`-2` 和 `-3` 都指向「手机号有问题」,但触发条件和处理方式不一样。`-5` 看起来也像号码问题,其实是号段库里没这条记录。这篇给出每个 `ret_code` 的实测触发条件,以及客户端该在哪一步拦。排查时对着表走,不用逐条试。
## 核心要点
- `ret_code` 非 0 即失败,失败时 `showapi_fee_num` 为 0,不消耗调用额度。
- 实测确认:长度不是硬校验。10 位和 12 位输入会照常返回成功,接口按前 7 位号段匹配。
- `-1` 是文档未登记、实测真实存在的状态:号段识别到了,归属地字段为空。
## 错误码对照
| `ret_code` | 官方含义 | 2026-09-18 实测触发条件 | `remark` |
|------------|----------|--------------------------|----------|
| 0 | 成功 | 前 7 位能在号段库中匹配 | 空 |
| -1 | 未登记 | `19999999999`,号段识别到电信但归属地字段全空 | 空 |
| -2 | 手机号输入的不是 11 位 | 长度 9 位及以下(`138`、`138001380`、`1380013`);含 `+` 或 `.`(`+8613800138000`、`13800138000.0`) | 电话号码输入格式有误 |
| -3 | 手机号中有非数字字符 | 含 ASCII 字母(`1380013800a`) | 手机号查询,num字段必须是11位数字 |
| -4 | 手机号格式错误 | 全角数字或中文数字(`13800138000`) | 电话号码输入格式有误 |
| -5 | 找不到归属地 | 11 位纯数字但号段不存在(`00000000000`、`12345678901`、`23800138000`) | 找不到手机归属地 |
| -6 | 找不到归属地 | 本批实测未触发,按 -5 同档处理 | — |
## `-2` 的真实判据不是「位数」
官方文档把 `-2` 描述为「手机号输入的不是 11 位」。实测下来,长度这一维比字面宽:
| 输入 | 位数 | `ret_code` | `showapi_fee_num` |
|------|------|------------|-------------------|
| `13800138000` | 11 | 0 | 1 |
| `1380013800` | 10 | 0 | 1 |
| `138001380000` | 12 | 0 | 1 |
| ` 13800138000`(前导空格) | 12 | 0 | 1 |
| `138-0013-8000`(连字符) | 14 | 0 | 1 |
| `138001380` | 9 | -2 | 0 |
| `138` | 3 | -2 | 0 |
| `13800138000.0` | 13 | -2 | 0 |
| `+8613800138000` | 14 | -2 | 0 |
10 位和 12 位照常返回成功,说明接口不按「必须 11 位」做长度卡口。它做的是先清理输入,再从里面取前 7 位去号段库匹配。带前导空格的输入能成功,说明首尾空白会被清掉;`138-0013-8000` 能成功,说明连字符会被清掉。`.` 和 `+` 不在清理范围内,于是落到 `-2`。
长度真正生效的地方是下界:9 位及以下凑不出一个 7 位号段,直接返回 `-2`。
## `-3` 和 `-4` 的分工
`-3` 只针对 ASCII 字母,`1380013800a` 返回 `-3`,`remark` 是「手机号查询,num字段必须是11位数字」。全角数字和中文数字走的是 `-4`:
```bash
# -3:含 ASCII 字母
curl -s "https://route.showapi.com/6-1?appKey=YOUR_APPKEY&num=1380013800a" --max-time 10
# -4:全角数字(需 URL 编码)
curl -s --get "https://route.showapi.com/6-1" \
--data-urlencode "appKey=YOUR_APPKEY" \
--data-urlencode "num=13800138000" \
--max-time 10
```
`-4` 这一类在全角输入法下会出现,前端把输入框的字符集限制成半角数字就能避免。
## `-5` 和有值的 `-1`
`-5` 表示 11 位纯数字、但号段库里没有这条记录。`00000000000`、`12345678901`、`23800138000` 都返回 `-5`,`remark` 为「找不到手机归属地」。
`-1` 是这次实测新看到的一档。输入 `19999999999` 时:
```json
{
"ret_code": -1,
"remark": "",
"num": 1999999,
"type": 2,
"name": "电信",
"prov": "",
"city": "",
"provCode": "",
"cityCode": "",
"areaCode": "",
"postCode": ""
}
```
`num`、`type`、`name` 有值,说明号段本身在库里;剩下 6 个归属地字段全是空字符串,说明这条号段还没有挂上省市信息,`showapi_fee_num` 是 0。解析代码把 `ret_code != 0` 统一当失败处理,这两种情况都不用单独分支。
## 客户端前置校验怎么写
因为接口按前 7 位号段匹配,客户端校验也按同一口径做,两边判据才一致。
```python
import re
def normalize_phone(raw: str) -> str | None:
"""清理输入并取前 7 位号段;格式不合规时返回 None。"""
if not isinstance(raw, str):
raw = str(raw)
cleaned = raw.strip().replace("-", "").replace(" ", "") # 与接口的清理范围对齐
if not re.fullmatch(r"\d{7,}", cleaned): # 至少 7 位纯半角数字
return None
if cleaned[0] == "0": # 首位为 0 的号段不存在
return None
return cleaned[:7]
def query_segment(seg: str) -> dict:
import requests
resp = requests.get(
"https://route.showapi.com/6-1",
params={"appKey": "YOUR_APPKEY", "num": seg},
timeout=10,
)
data = resp.json()
body = data.get("showapi_res_body", {})
if body.get("ret_code") != 0:
# -2/-3/-4 属于输入问题,-1/-5/-6 属于号段问题,分开记录便于定位
return {"ok": False, "ret_code": body.get("ret_code"), "remark": body.get("remark")}
return {"ok": True, **{k: body.get(k) for k in
("prov", "city", "name", "type", "num", "areaCode", "postCode")}}
```
Node.js 侧同样的判据:
```javascript
const normalizePhone = (raw) => {
const cleaned = String(raw).trim().replace(/[-\s]/g, "");
if (!/^\d{7,}$/.test(cleaned)) return null;
if (cleaned[0] === "0") return null;
return cleaned.slice(0, 7); // 与接口口径一致:只用前 7 位
};
const seg = normalizePhone("13800138000");
const resp = await fetch(
`https://route.showapi.com/6-1?appKey=YOUR_APPKEY&num=${seg}`,
{ signal: AbortSignal.timeout(10000) }
);
const { showapi_res_body: body } = await resp.json();
console.log(body.ret_code === 0 ? body : `失败 ${body.ret_code} ${body.remark}`);
```
## 排查路径
1. `ret_code` 是 `-2`、`-3`、`-4`:问题在输入侧。先看输入的字符是不是全半角数字,再看有没有混进 `+`、`.` 这类未被清理的符号。
2. `ret_code` 是 `-5`、`-6`:输入格式没问题,号段库里没有这条记录。常见于测试用的假号码。
3. `ret_code` 是 `-1`:号段存在、归属地字段为空。按失败处理,不要拿空字符串去拼展示文案。
4. `showapi_res_code` 是 -1、`showapi_res_error` 是 `must input num field`:请求里没带 `num`,检查参数拼装。空字符串 `num=` 也走这一档。
## FAQ
**Q1:`-2` 一定是位数不对吗?**
不一定。实测 10 位和 12 位输入都会成功返回,`-2` 的实测触发条件是长度 9 位及以下,或者输入里含有 `+`、`.` 这类未被清理的符号。
**Q2:为什么 `138-0013-8000` 能查出来?**
接口会清理输入里的空格和连字符,再用剩下的数字取前 7 位匹配。`.` 和 `+` 不在清理范围内,带这两种符号的输入会返回 `-2`。
**Q3:失败会扣调用次数吗?**
不会。失败时 `showapi_fee_num` 为 0,文档口径是失败不扣点数。
**Q4:`-5` 和 `-1` 有什么区别?**
`-5` 是号段库里没有这条记录,只有 `remark` 有值。`-1` 是号段在库里但归属地字段为空,`num`、`type`、`name` 有值,其余字段是空字符串。两者同为失败。
**Q5:`-6` 什么时候出现?**
本批实测没有触发过 `-6`,它和 `-5` 在文档里同属「找不到归属地」。解析时把两者归到同一档处理即可。
**Q6:网络超时会返回什么 `ret_code`?**
超时不会产生 `ret_code`,请求根本没拿到响应。官方 OpenAPI 标注连接与读取超时都是 10 秒,客户端超时按 10 秒设置,并在代码里单独捕获超时异常。
## 下一步阅读
- [手机归属地查询返回字段全解:11 个字段的类型、取值与粒度](https://www.showapi.com/guides/phone-attribution-response-fields-6) —— `ret_code` 在整份返回里的位置
- [手机归属地查询接口怎么用?从 AppKey 到第一条真实返回的完整路径](https://www.showapi.com/guides/phone-attribution-quickstart-6) —— 请求拼装和两层状态码
- [手机归属地查询按前 7 位号段做缓存:实测粒度与 Redis 实现](https://www.showapi.com/guides/phone-attribution-cache-6) —— 把前 7 位用进缓存键
- **本系列共 12 篇**:查看[手机归属地查询指南总目录](https://www.showapi.com/guides/phone-attribution-guides-6)





