企业抬头与税号查询
名字查税号、税号查名字,两个方向都支持,统一返回企业全称 + 纳税人识别号。 最典型的用途是开票时的抬头自动补全——用户敲「北京数影」,下拉列出候选,选中后全称和税号一起填进去,不用手输、也不会输错; 反过来,手里已有税号要回填抬头或做校验,直接用税号查即可。
接口
POST https://openapi.kailingteck.com/api/v1/enterprise/search
鉴权:X-App-Key + X-Timestamp(Unix 秒) + X-Sign(= sha256(appKey + 时间戳 + 报文原文 + appSecret) 小写十六进制)。与开灵其它接口完全一致。
请求参数
一个接口,两种查法。地址、鉴权都一样,区别只在于你传哪个字段:
① 知道名字,不知道税号
客户开票时只记得公司名的一部分 → 传 name,返回一批候选让他挑。
{ "name": "北京数影" }→ 返回多条,让用户选一条
② 知道税号,要查名字
手里有税号,要回填抬头或做校验 → 传 tax_id,直接给你那一家。
{ "tax_id": "91110105MA01ECN70Q" }→ 返回 1 条,或 0 条表示查无此号
★两个字段二选一,不能同时传,也不能都不传,否则返回 400。 返回里的 mode 会告诉你本次走的是哪一种。
| 字段 | 必填 | 大白话说明 |
|---|---|---|
name | 二选一 | ★按名称模糊查:填企业名称关键词,2~64 个字符。3 字以上支持跨词匹配(「数影科技」能命中「北京数影互联科技有限公司」) |
tax_id | 二选一 | ★按税号精确查:填完整纳税人识别号,15~20 位数字或字母。小写会自动转大写,不用自己处理 |
limit | 选填 | 最多返回几条,1~30,默认 30。按税号查只会返回 1 条,这个字段用不上 |
请求示例 · 按名称模糊查
{
"name": "北京数影",
"limit": 10
}请求示例 · 按税号精确查
{
"tax_id": "91110105MA01ECN70Q"
}返回
统一信封 code/msg/requestId/timestamp/data,业务字段都在 data 里。
返回参数(data)
| 字段 | 大白话说明 |
|---|---|
mode | 本次走的是哪种查法:name=按名称模糊查 / tax_id=按税号精确查 |
total | 本次返回的条数。按税号查命中就是 1,查不到就是 0 |
list[].company_name | 企业全称 |
list[].tax_id | 纳税人识别号(统一社会信用代码) |
cached | true=命中平台缓存(结果一样,只是没再打上游)。注意:命中缓存同样计费 |
isMock | true=演示数据,不是真实工商数据 |
返回示例(成功)
{
"code": "CYT_00000",
"msg": "success",
"requestId": "cyt_xxx",
"data": {
"mode": "name",
"total": 2,
"list": [
{ "tax_id": "91310120MA1HYQR37H", "company_name": "上海和开灵科技中心" },
{ "tax_id": "91310120MABREQ9M6W", "company_name": "上海开灵辉盈商贸有限公司" }
],
"cached": false,
"isMock": false
}
}返回示例(按税号精确查)
{
"code": "CYT_00000",
"msg": "success",
"data": {
"mode": "tax_id",
"total": 1,
"list": [
{ "tax_id": "91110105MA01ECN70Q", "company_name": "北京数影互联科技有限公司" }
],
"cached": false,
"isMock": false
}
}返回示例(查不到)
{
"code": "CYT_00000",
"msg": "success",
"data": { "mode": "tax_id", "total": 0, "list": [], "cached": false, "isMock": false }
}异常与报错
| 情形 | 含义与处理 |
|---|---|
400 · CYT_40001 | 三种情况:name 和 tax_id 都没传/两个都传了/格式不对(name 不在 2~64 字,或 tax_id 不是 15~20 位)。读 data.hint 有具体原因 |
401 · CYT_40100 / 40102 | 缺鉴权头,或签名算错 |
401 · CYT_40103 | 时间戳与服务器相差超过 300 秒 |
403 · CYT_40301 | 该密钥不是「企业开票名称模糊查询」产品的 |
429 · CYT_42901 | 超过每分钟 120 次限流 |
502 · CYT_50200 | 企业信息源暂时不可用。平台已自动重试过一次,仍失败才返回此码,稍后重试即可 |
| 查不到公司 | 返回 total=0、list 为空数组。这是正常返回,不是错误,应允许用户手动输入 |
做输入联想时必看(否则费用会失控)
本接口按调用次数计费,调一次算一次。如果要做「边打字边联想」, 不做防抖的话用户填一次表可能触发几十次调用。三件事一定要做:
- 防抖:输入停止 300 毫秒后再发请求,不要每敲一个字就发
- 少于 2 个字不查:上游要求 name ≥ 2 字,少于 2 字直接不发
- 前端也留一层缓存:同一个词查过就别再查
平台侧已有 10 分钟服务端缓存,但那省的是上游额度,对你仍按调用次数计费,所以前端防抖才是真正省钱的地方。
let timer, lastQuery = '', memo = new Map();
input.oninput = () => {
const kw = input.value.trim();
clearTimeout(timer);
if (kw.length < 2 || kw === lastQuery) return; // 少于2字 / 重复词,不查
if (memo.has(kw)) return render(memo.get(kw)); // 前端缓存
timer = setTimeout(async () => { // 防抖 300ms
lastQuery = kw;
const r = await searchEnterprise(kw);
memo.set(kw, r.data.list);
render(r.data.list);
}, 300);
};