手机归属地查询返回字段全解:11 个字段的类型、取值与粒度
约 15 分钟 入门手机号码归属地查询返回字段说明字段类型数据结构
# 手机归属地查询返回字段全解:11 个字段的类型、取值与粒度
接口:手机归属地查询(apiCode=6,接入点 6-1)|是否免费:是,注册后默认可调用,设使用档次限制|请求方式:POST / GET|返回格式:JSON|适用人群:要把返回字段映射进数据库或结构体的开发者|阅读时间:约 9 分钟|最后实测核对:2026-09-18
官方 OpenAPI 参数表登记了 9 个返回字段,真实返回里有 11 个。多出来的 `postCode` 和 `remark` 不在文档里,但每次都在。这篇把 11 个字段的类型、真实取值和能拿来干什么讲完,后面写解析代码就不用回来查。
## 核心要点
- 业务字段一共 11 个:`prov` `city` `name` `num` `provCode` `cityCode` `areaCode` `postCode` `type` `ret_code` `remark`,全部在 `showapi_res_body` 里。
- 官方 OpenAPI 参数表只登记了 9 个,`postCode` 和 `remark` 未登记,但真实返回里带着。
- `name` 不是纯运营商名,实测返回的是套餐或品牌名,做展示别直接拿它当运营商标签。
## 返回结构:先剥一层
外层是网关字段,内层才是业务数据。
```
HTTP 响应 JSON
├── showapi_res_code 网关级状态码,0 表示网关层正常
├── showapi_res_error 网关级错误信息
├── showapi_res_id 本次请求唯一 ID
├── showapi_fee_num 本次调用计费次数
└── showapi_res_body ★ 业务数据全部在这里 ★
├── prov / city / name / num
├── provCode / cityCode / areaCode / postCode
├── type / ret_code
└── remark
```
直接读 `data.prov` 会拿到 `undefined` 或 `KeyError`,因为省名在 `data.showapi_res_body.prov`。
## 字段逐个说明
下表类型以 2026-09-18 实测返回为准,示例值取同一批实测数据。
| 字段 | 实测类型 | 含义 | 实测示例值 |
|------|----------|------|------------|
| `prov` | String | 省 | 北京 |
| `city` | String | 市 | 北京 |
| `name` | String | 运营商名称 | 移动全球通卡 |
| `num` | Number | 号段,前 7 位 | 1380013 |
| `provCode` | String | 省别编码,本省身份证前几位 | 110000 |
| `cityCode` | String | 城市编码,本城市身份证前几位 | 110100 |
| `areaCode` | String | 城市区号,座机号码前几位 | 010 |
| `postCode` | String | 邮政编码 | 100000 |
| `type` | Number | 运营商枚举 | 1 |
| `ret_code` | Number | 业务状态码 | 0 |
| `remark` | String | 补充说明,失败时给出原因 | 空字符串 |
官方 OpenAPI 里 `provCode` 和 `cityCode` 标的是 Number,实测返回的是字符串。`areaCode` 和 `postCode` 带前导零,写成 `"010"`、`"100000"`。这三个字段在入库时按字符串存,不要走数值转换,否则区号会变成 `10`、邮编前导零会丢。
`postCode` 和 `remark` 属于文档参数表未登记、真实返回存在的字段。判断解析代码健壮性时按「可能出现也可能不带」处理,统一用 `.get()` 取值。
## `name` 字段的实际含义
文档把 `name` 描述为「运营商」,实测返回值的形态比这个宽。同一批实测里出现过这些值:
| 输入号段 | 返回的 `name` | 返回的 `type` |
|----------|---------------|---------------|
| 1380013 | 移动全球通卡 | 1 |
| 1860018 | 联通186卡 | 3 |
| 1890018 | 中国电信天翼卡 | 2 |
| 1920000 | 192卡 | 4 |
| 1970000 | 移动 | 1 |
| 1999999 | 电信 | 2 |
同一个运营商可能返回「移动全球通卡」,也可能只返回「移动」。要按运营商走分支逻辑,用 `type` 的数字值,`name` 放在界面上展示。两个字段是同一件事的两种表达,不用各存一份再做比对。
## `type` 运营商枚举
| `type` 值 | 运营商 | 实测样本 |
|-----------|--------|----------|
| 1 | 移动 | 1380013、1970000 |
| 2 | 电信 | 1890018、1999999 |
| 3 | 联通 | 1860018 |
| 4 | 广电 | 1920000(广东佛山) |
| -1 | 未知 | 本批实测未取到该值 |
具体号段归哪家以接口返回为准。新号段会陆续放出来,写死一张号段对照表迟早要改,判断运营商请用 `type`。
`type` 的完整用法另有一篇:[手机归属地查询 type 字段全解](https://www.showapi.com/guides/phone-attribution-type-field-6)。
## `ret_code` 业务状态码
`ret_code` 在 `showapi_res_body` 内,跟外层的 `showapi_res_code` 是两回事。
| `ret_code` | 含义 | 文档是否登记 |
|------------|------|--------------|
| 0 | 成功 | 是 |
| -1 | 号段已识别、归属地字段为空 | 否,实测发现 |
| -2 | 手机号输入的不是 11 位 | 是 |
| -3 | 手机号中有非数字字符 | 是 |
| -4 | 手机号格式错误 | 是 |
| -5 / -6 | 找不到归属地 | 是 |
2026-09-18 实测 `num=19999999999` 时返回 `ret_code = -1`,`num`、`type`、`name` 有值,`prov`、`city`、`areaCode`、`postCode`、`provCode`、`cityCode` 全为空字符串。这一档官方参数表里没有,解析时把非 0 的 `ret_code` 统一当失败处理即可覆盖。同一批实测未取到 -6,实际业务里按 -5 同档处理。
排查路径见 [手机归属地查询错误码逐条对照](https://www.showapi.com/guides/phone-attribution-error-codes-6)。
## `provCode` 和 `cityCode` 能干什么
官方注释写的是「本省身份证的前几位编码」。实测 `13800138000` 落在北京,`provCode` 是 `110000`、`cityCode` 是 `110100`,跟北京身份证前 6 位一致。
这两个字段适合做一件事:把手机号归属地和用户填写的地域信息做同源比对。比如注册时用户选的省份是广东,手机号 `provCode` 是 `110000`,两者不一致就打个标记。
它们不承担身份证校验的职责,那属于另一个接口的范围。
## 解析代码
```python
import requests
def query(phone: str) -> dict:
"""返回归一化后的字段,失败时抛异常。"""
resp = requests.get(
"https://route.showapi.com/6-1",
params={"appKey": "YOUR_APPKEY", "num": phone},
timeout=10,
)
data = resp.json()
if data.get("showapi_res_code") != 0:
raise RuntimeError(data.get("showapi_res_error"))
body = data.get("showapi_res_body", {})
if body.get("ret_code") != 0:
raise RuntimeError(f"ret_code={body.get('ret_code')} {body.get('remark', '')}")
return {
"prov": body.get("prov"), # 省
"city": body.get("city"), # 市
"carrier": body.get("name"), # 展示用,形态不固定
"carrier_type": body.get("type"), # 分支逻辑用这个
"seg": body.get("num"), # 号段,前 7 位
"area_code": body.get("areaCode"), # 字符串,含前导零
"post_code": body.get("postCode"), # 字符串,参数表未登记
"prov_code": body.get("provCode"), # 身份证前 6 位口径
"city_code": body.get("cityCode"),
}
```
同一个解析逻辑用 cURL 和 Node.js 也能跑:
```bash
curl -s "https://route.showapi.com/6-1?appKey=YOUR_APPKEY&num=13800138000" \
--max-time 10 \
| python -c "import sys,json;b=json.load(sys.stdin)['showapi_res_body'];print(b['prov'],b['city'],b['name'],b['num'],b['areaCode'],b['postCode'])"
```
```javascript
const resp = await fetch(
"https://route.showapi.com/6-1?appKey=YOUR_APPKEY&num=13800138000",
{ signal: AbortSignal.timeout(10000) }
);
const { showapi_res_body: body } = await resp.json();
const { prov, city, name, type, num, areaCode, postCode } = body;
console.log({ prov, city, name, type, num, areaCode, postCode }); // 结构化字段一次取全
```
## FAQ
**Q1:`showapi_res_code` 和 `ret_code` 有什么区别?**
`showapi_res_code` 在响应最外层,反映网关层状态;`ret_code` 在 `showapi_res_body` 里,反映业务查询结果。判断查询成不成功看 `ret_code`。
**Q2:为什么读 `data.prov` 读不到?**
省名在 `data.showapi_res_body.prov`,比外层多一层。这是最常见的取值位置错误。
**Q3:`num` 返回的是用户输入的完整号码吗?**
不是。`num` 是号段,只取前 7 位。实测输入 `13800138000`,返回的 `num` 是 `1380013`。完整号码不会回传。
**Q4:`postCode` 参数表里没有,能依赖它吗?**
能用,但要按可选字段处理。它出现在真实返回里,只是官方 OpenAPI 参数表未登记。解析时用 `.get("postCode")` 兜底,缺失时不强制依赖。
**Q5:区号存成整数为什么会变?**
`areaCode` 是字符串,`"010"` 这样的值带前导零。转成整数后变成 `10`,再拼回展示就错了。这类字段在库里用字符串类型。
**Q6:`name` 和 `type` 哪个更适合做业务判断?**
业务分支用 `type`,界面展示用 `name`。实测 `name` 会返回「移动全球通卡」「联通186卡」这类品牌名,形态不固定;`type` 的取值是稳定的数字枚举。
**Q7:能用 `provCode` 做身份证归属地校验吗?**
不能。文档只说它是「本省身份证的前几位编码」,可用于手机号归属地与用户填写地域的同源比对。身份证校验是另一个接口的职责。
## 下一步阅读
- [手机归属地查询接口怎么用?从 AppKey 到第一条真实返回的完整路径](https://www.showapi.com/guides/phone-attribution-quickstart-6) —— 先把请求打通
- [手机归属地查询返回 -2、-3、-4 是什么意思?错误码逐条对照与排查](https://www.showapi.com/guides/phone-attribution-error-codes-6) —— 非 0 状态码怎么处理
- [手机归属地查询按前 7 位号段做缓存:实测粒度与 Redis 实现](https://www.showapi.com/guides/phone-attribution-cache-6) —— 号段粒度怎么用进缓存键
- **本系列共 12 篇**:查看[手机归属地查询指南总目录](https://www.showapi.com/guides/phone-attribution-guides-6)





