技术博客
银行卡归属地查询:用 Python 跑通 30-7 接口的第一条请求

银行卡归属地查询:用 Python 跑通 30-7 接口的第一条请求

作者: 万维易源
2026-09-15
银行卡归属地查询API快速接入Python示例nodejs示例
# 银行卡归属地查询:用 Python 跑通 30-7 接口的第一条请求 > 接口:银行卡归属地查询(apiCode=30)· 接入点:`30-7` 银行卡归属信息查询 · 计费:按次计费,5 厘/次,查询失败不计费 · 请求方式:GET / POST · 返回格式:JSON · 适用人群:首次接入的开发者 · 阅读时间:约 6 分钟 > 最后实测核对:2026-09-15 ## 核心要点 - 接口地址是 `https://route.showapi.com/30-7`,AppKey 放在 query 参数 `appKey` 里,一次请求拿到归属地、银行名、卡种、卡品牌、客服电话与官网。 - 返回体是两层:外层 `showapi_res_code` 判请求有没有被受理,内层 `showapi_res_body.ret_code` 判这次有没有查到结果。两层都得看。 - 要 BIN 码和银联 Luhn 效验结果就加 `needBin=1`;不加,那四个字段整体不出现。 ## 为什么绑卡环节常常缺这一步 用户在收银台填完卡号,你的系统手里只有一串数字。这串数字属于哪家银行、是借记卡还是信用卡、开户地在哪,都得再查一次。 这些信息有三个直接用途。对账时把卡号翻译成行名,省掉人工逐条核;风控时看卡种是否落在业务允许的范围内;用户端把银行 logo 和客服电话显示出来,用户会更有把握。 自己维护一份 BIN 段表也能做,代价是每年不定期变化的银行和卡品牌得自己跟。银行卡归属地查询把这些放在一个 HTTP 接口后面,业务侧只需要一次请求。 ## 前置条件 - 一个 ShowAPI 账号,在[控制台](https://www.showapi.com/console#/myApp)拿到 AppKey。 - Python 环境(示例用 `requests`;cURL 和 Node.js 版本在下面同样给出)。 - 一次可用的调用额度。本接口按次计费,5 厘/次;查询失败的请求不计费。 ## 接口速览 | 项 | 值 | |------|------| | 接口地址 | `https://route.showapi.com/30-7?appKey={your_appKey}` | | 请求方式 | GET / POST | | 鉴权 | `appKey` 走 query 参数 | | 请求参数 | `cardNum`(必填)、`needBin`(可选) | | 返回格式 | JSON | | 计费 | 5 厘/次,专用资源包 50 元 = 本接入点 1 万次;失败不计费 | | 并发 | 10 次/秒 | | 数据更新 | 每年不定期更新 | | 集成方式 | MCP 服务、OpenAPI 3.0 文档、在线调试 | ## 四步跑通 ### 第一步:拿到 AppKey 登录后在控制台创建应用,复制 AppKey。下面的代码里统一用占位符 `YOUR_APPKEY`。 ### 第二步:发一条请求 最省事的是 GET,参数全在 URL 上。 ```bash curl -s "https://route.showapi.com/30-7?appKey=YOUR_APPKEY&cardNum=6228480402564890018&needBin=1" \ --max-time 30 ``` POST 表单的话,参数放 body,header 指定 `content-type: application/x-www-form-urlencoded`: ```bash curl -s -X POST "https://route.showapi.com/30-7?appKey=YOUR_APPKEY" \ -H "content-type: application/x-www-form-urlencoded" \ -d "cardNum=6228480402564890018&needBin=1" \ --max-time 30 ``` ### 第三步:解析两层返回 Python 版本,带上超时和两层的错误处理: ```python import requests APPKEY = "YOUR_APPKEY" API_URL = "https://route.showapi.com/30-7" def query_bank_card(card_num: str, need_bin: bool = False, timeout: int = 30) -> dict: """查询银行卡归属信息。超时按 OpenAPI 文档标注的 30 秒设置。""" params = {"appKey": APPKEY, "cardNum": card_num} if need_bin: params["needBin"] = "1" resp = requests.get(API_URL, params=params, timeout=timeout) resp.raise_for_status() data = resp.json() # 第一层:外层状态码为 0 才说明请求被受理; # 参数缺失等情况下,外层直接返回 -1 且 showapi_res_body 为空对象。 if data.get("showapi_res_code") != 0: raise RuntimeError(f'请求失败:{data.get("showapi_res_error")}') body = data.get("showapi_res_body") or {} # 第二层:ret_code 为 0 表示查到结果。实测返回的是整数,文档标注为 String, # 这里统一转成字符串比较,两种形态都能覆盖。 if str(body.get("ret_code")) != "0": raise ValueError(f'未查到归属地:{body.get("remark", "无附加说明")}') return body if __name__ == "__main__": info = query_bank_card("6228480402564890018", need_bin=True) print(info["formatBankName"], "|", info["area"], "|", info["cardType"]) # 农业银行 | 江苏 - 苏州 | 借记卡 ``` Node.js 版本: ```javascript const APPKEY = "YOUR_APPKEY"; const API_URL = "https://route.showapi.com/30-7"; async function queryBankCard(cardNum, needBin = false) { const url = new URL(API_URL); url.searchParams.set("appKey", APPKEY); url.searchParams.set("cardNum", cardNum); if (needBin) url.searchParams.set("needBin", "1"); const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), 30000); // 对齐 OpenAPI 的 x-read-timeout try { const res = await fetch(url, { signal: controller.signal }); const data = await res.json(); if (data.showapi_res_code !== 0) { throw new Error(`请求失败:${data.showapi_res_error}`); } const body = data.showapi_res_body || {}; if (String(body.ret_code) !== "0") { throw new Error(`未查到归属地:${body.remark || "无附加说明"}`); } return body; } finally { clearTimeout(timer); } } queryBankCard("6228480402564890018", true) .then((b) => console.log(b.formatBankName, "|", b.area, "|", b.cardType)) .catch(console.error); ``` ### 第四步:把结果渲染出来 `logo` 和 `formatBankName` 直接可以用在绑定成功的提示里: ```html <div class="bank-card-result"> <img id="bank-logo" alt="银行标志" width="32" height="32" /> <span id="bank-name"></span> <span id="bank-area"></span> <span id="bank-tel"></span> </div> <script> // info 为上面接口返回的 showapi_res_body document.getElementById("bank-logo").src = info.logo; document.getElementById("bank-name").textContent = info.formatBankName || info.bankName; document.getElementById("bank-area").textContent = info.area; document.getElementById("bank-tel").textContent = info.tel; </script> ``` `logo` 在部分银行返回空字符串,`formatBankName` 同理。展示前判一次空值,回退到 `bankName`。 ## 真实返回长什么样 下面这条是 2026-09-15 用 `cardNum=6228480402564890018&needBin=1` 实际调用拿到的完整响应: ```json { "showapi_res_error": "", "showapi_res_id": "6aa8e433fb638c2f69478484", "showapi_res_code": 0, "showapi_fee_num": 1, "showapi_res_body": { "logo": "http://static1.showapi.com/app2/banklogo/abc.png", "cardNum": "6228480402564890018", "area": "江苏 - 苏州", "cardType": "借记卡", "bankName": "中国农业银行", "formatBankName": "农业银行", "isLuhn": "1", "card_digits": "19", "brand": "金穗通宝卡(银联卡)", "simpleCode": "ABC", "bin_digits": "6", "url": "www.abchina.com", "card_bin": "622848", "tel": "95599", "ret_code": 0 } } ``` 三个可以直接对着看的点: - `area` 的实际格式是 `江苏 - 苏州`,中间是「空格-空格」。帮助手册里 646 个归属地取值都是这个写法。 - `formatBankName` 是 `农业银行`,`bankName` 是 `中国农业银行`。要做字典表或下拉框,用 `formatBankName` 更整齐。 - `showapi_fee_num` 为 `1`,表示这次调用计了 1 次。 字段逐个说明看[银行卡归属地查询返回字段逐项说明](https://www.showapi.com/guides/bank-card-attribution-response-fields-30)。 ## 参数怎么传 `cardNum` 必填,传银行卡号。`needBin` 可选,传 `1` 会多返回 BIN 码、BIN 码长度、卡号长度和 Luhn 效验结果;传 `0` 或不传,这四个字段整体不出现。 `needBin=1` 会让响应变慢一些,接口文档对此的说明是「返回这些信息将使得查询更加耗时」。只在需要做卡号形态校验时打开。 两个参数的详细用法与组合建议见[银行卡归属地查询的 cardNum 与 needBin 怎么传](https://www.showapi.com/guides/bank-card-attribution-params-guide-30)。 ## FAQ **Q1:一条请求扣几次费?** 按调用次数计费,一次成功调用扣 1 次,单价 5 厘。2026-09-15 实测:成功调用返回 `showapi_fee_num: 1`;参数缺失、卡号未收录、归属地未收录这三种失败情况,`showapi_fee_num` 都是 `0`。 **Q2:为什么我拿到的 `logo` 是空的?** `logo` 与 `formatBankName` 两个字段在部分银行会返回空字符串。展示时判空后回退到 `bankName` 或本地默认图即可。 **Q3:返回的 `cardNum` 是脱敏的吗?** 不是。返回体里的 `cardNum` 与请求传入的卡号一致。写日志或落库时按你所在业务的脱敏规范处理。 **Q4:接口地址里的接入点编号为什么是 30-7?** `30` 是接口编号,`7` 是本接口唯一的接入点编号。参数页真实入口是 [https://www.showapi.com/apiGateway/view/30](https://www.showapi.com/apiGateway/view/30) 和 [https://www.showapi.com/apiGateway/view/30/7](https://www.showapi.com/apiGateway/view/30/7)。 **Q5:能一次查多张卡吗?** 本接口只有 `30-7` 一个接入点,一次请求对应一个卡号。批量场景需要在调用方循环,并注意 10 次/秒的并发上限。 ## 下一步阅读 - [银行卡归属地查询返回字段逐个说清](https://www.showapi.com/guides/bank-card-attribution-response-fields-30)——15 个字段哪个必回、哪个条件返回、空了怎么办 - [银行卡归属地查询排错:三种失败形态怎么区分](https://www.showapi.com/guides/bank-card-attribution-error-codes-30)——只判 `ret_code` 会漏掉两种失败 - [支付收银台接入银行卡归属地查询](https://www.showapi.com/guides/bank-card-attribution-payment-risk-30)——绑卡链路的完整设计 - **本系列共 10 篇**:查看[银行卡归属地查询指南总目录](https://www.showapi.com/guides/bank-card-attribution-guides-30)