手机归属地查询返回字段全解: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)
加载中...