银行卡归属地查询:用 Python 跑通 30-7 接口的第一条请求
银行卡归属地查询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)