一个投诉引发的血案
去年双十一前一周,我负责的短信平台突然涌入大量客户投诉。一个做电商的客户直接在群里炸了:"你们发的验证码短信,用户根本收不到!投诉率飙到 8%!"我当时第一反应是通道出问题了,查了半天通道状态一切正常。最后才发现——客户的发送号码被好几个手机厂商的标记系统标记成了"骚扰电话",用户看到来电显示直接拒接,短信通知也跟着被拦截。
那是我第一次真正意识到号码标记这个问题的严重性。一个号码被标记,影响的不仅仅是电话触达率,连带着短信、语音验证码的到达率都会暴跌。后来我花了三个月时间,把市面上主流的号码标记查询 API 全部对接了一遍,踩了无数坑。今天把这些经验整理出来,希望能帮到同样在做号码认证和风控的朋友。
号码标记到底是个什么生态
先说个很多人不知道的事实:国内号码标记数据是高度碎片化的。没有一家平台能覆盖所有标记数据。小米有小米的标记库,华为有华为的,腾讯手机管家又是一套,360 又是另一套。一个号码可能只在华为上被标记了"快递外卖",但在小米上干干净净。所以你要查一个号码的标记状态,只查一家根本不够。
这也是为什么号码标记查询 API 这么重要——你需要同时查询多个平台的数据,才能给出一个相对准确的标记画像。而手机号码标记清除就更麻烦了,你得在每个平台分别提交申诉,有些平台的申诉入口藏得比彩蛋还深。
目前市面上主流的号码标记数据源主要有这些:小米标记、腾讯手机管家、华为号码、360 手机卫士、百度手机卫士、泰迪熊、电话邦、阿里号码认证,加上中国移动的和彩印。这 9 家基本覆盖了国内 90% 以上的手机用户。
9 大平台实测对比
我在生产环境对这 9 个平台的号码标记查询 API 做了为期两个月的持续监控,测试样本是 50 万个真实号码。以下是核心数据:
- 腾讯手机管家:平均响应时间 82ms,标记覆盖率 73.2%,API 稳定性 99.7%。腾讯的数据质量确实好,覆盖率高,误标率控制在 2.1% 左右。缺点是接入门槛高,需要企业认证,审核周期 5-7 个工作日。
- 华为号码:平均响应时间 95ms,标记覆盖率 68.5%,API 稳定性 99.5%。华为的标记数据在自家手机上权重最高,如果你的用户群体华为占比大,这个必须接。API 文档写得也不错,对接比较顺畅。
- 小米标记:平均响应时间 68ms,标记覆盖率 61.3%,API 稳定性 99.6%。小米的响应速度是最快的,接口设计也很简洁。但覆盖率偏低,很多标记数据依赖用户上报,数据新鲜度有时候不够。
- 360 手机卫士:平均响应时间 143ms,标记覆盖率 65.8%,API 稳定性 98.9%。360 的响应时间偏慢,偶尔会出现超时。但它的标记库历史最久,一些老号码的标记数据只有 360 有。
- 百度手机卫士:平均响应时间 112ms,标记覆盖率 58.7%,API 稳定性 99.1%。百度的覆盖率不太行,但它的标记分类做得细,能区分出"房产中介""教育培训"这种细分标签,其他平台大多只标"骚扰电话"。
- 泰迪熊:平均响应时间 210ms,标记覆盖率 71.6%,API 稳定性 97.3%。泰迪熊的覆盖率很高,仅次于腾讯,但 API 稳定性是所有平台里最差的。这个问题我后面专门讲。
- 电话邦:平均响应时间 126ms,标记覆盖率 64.2%,API 稳定性 99.2%。电话邦的数据来源比较独特,跟运营商有合作,一些运营商侧的标记数据只有它有。
- 阿里号码认证:平均响应时间 78ms,标记覆盖率 55.4%,API 稳定性 99.8%。阿里响应快、稳定性好,但覆盖率偏低。它的优势是跟阿里生态打通,淘宝、支付宝的号码展示会用到它的数据。
- 中国移动和彩印:平均响应时间 156ms,标记覆盖率 52.1%,API 稳定性 99.0%。移动的覆盖率最低,但它是运营商官方数据,权威性最高。如果你的业务需要运营商级别的标记认证,这个不可替代。
总结一下:如果只选 3 家,我推荐腾讯 + 华为 + 泰迪熊,这三家加起来覆盖率能到 89% 左右。如果预算和精力允许,建议 9 家全接,用聚合查询的方式做。
泰迪熊 API 不稳定的血泪教训
单独把泰迪熊拎出来说,是因为它真的给我造成了生产事故。
泰迪熊的号码标记 API 有个很诡异的问题——它不是完全不可用,而是间歇性地超时。你连续请求 100 次,可能 97 次都在 200ms 内返回,但突然就来 3 次超时,超时时间长达 5-8 秒。这种问题在测试环境根本发现不了,因为测试的时候请求量小,碰不上。但到了生产环境,QPS 一上来,问题就暴露了。
我们上线第一周,泰迪熊 API 的超时直接把整个号码标记查询服务的 P99 响应时间拉到了 3.2 秒。用户侧的表现就是——页面加载的时候那个"号码安全检测"的 loading 转了半天转不完。
最后的解决方案是:给每个平台的 API 调用都加上独立的超时控制和降级策略。泰迪熊的超时阈值设成 500ms,超时直接返回空数据,不阻塞其他平台的查询。同时加上指数退避的重试机制,最多重试 2 次。改完之后,整体 P99 从 3.2 秒降到了 280ms。
代码大概长这样:
async function queryWithTimeout(apiFn, timeout = 500) {
const result = await Promise.race([
apiFn(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), timeout)
)
]);
return result;
}
async function queryWithRetry(apiFn, maxRetries = 2, timeout = 500) {
for (let i = 0; i <= maxRetries; i++) {
try {
return await queryWithTimeout(apiFn, timeout);
} catch (e) {
if (i === maxRetries) return null;
await new Promise(r => setTimeout(r, Math.pow(2, i) * 100));
}
}
}这个模式我现在对所有第三方 API 调用都用,不限于号码标记。经验就是:永远不要信任第三方 API 的稳定性,哪怕它承诺 99.99%。
聚合查询方案的设计
前面说了,号码标记数据是碎片化的,所以聚合查询是必选项。我的方案是用 Promise.allSettled 并行查询所有平台,然后合并结果。
为什么用 allSettled 而不是 all?因为 all 的话,任何一个平台报错,整个 Promise 就 reject 了。而 allSettled 会等所有 Promise 都 settled,不管成功还是失败,你可以拿到每个平台的结果单独处理。这对号码标记查询特别重要——哪怕 9 个平台里有 3 个挂了,剩下的 6 个数据依然有价值。
async function aggregateMarkQuery(phoneNumber: string) {
const platforms = [
{ name: '腾讯', fn: () => queryWithRetry(queryTencent, 2, 500) },
{ name: '华为', fn: () => queryWithRetry(queryHuawei, 2, 500) },
{ name: '小米', fn: () => queryWithRetry(queryXiaomi, 2, 500) },
{ name: '360', fn: () => queryWithRetry(query360, 2, 500) },
{ name: '百度', fn: () => queryWithRetry(queryBaidu, 2, 500) },
{ name: '泰迪熊', fn: () => queryWithRetry(queryTeddy, 2, 500) },
{ name: '电话邦', fn: () => queryWithRetry(queryDianhua, 2, 500) },
{ name: '阿里', fn: () => queryWithRetry(queryAli, 2, 500) },
{ name: '移动', fn: () => queryWithRetry(queryMobile, 2, 500) },
];
const results = await Promise.allSettled(
platforms.map(p => p.fn().then(data => ({ platform: p.name, data })))
);
const marks = results
.filter((r): r is PromiseFulfilledResult => r.status === 'fulfilled' && r.value.data)
.map(r => r.value);
return mergeMarks(marks);
} 合并结果的时候有个细节要注意:不同平台的标记分类标准不一样。腾讯标的是"推销",华为可能标的是"广告",360 标的是"骚扰电话"。你得做一个标记分类的映射表,把这些不同的标签统一成你们系统内部的标准分类。不然前端展示的时候,同一个号码一会儿显示"推销"一会儿显示"广告",用户会懵。
还有一个坑是标记冲突处理。一个号码在腾讯上标的是"快递外卖",在 360 上标的是"骚扰电话",你信谁?我的策略是:如果超过半数平台的标记一致,就采用多数结果;如果标记分散,就取标记来源权重最高的那个(腾讯和华为的权重最高)。这个策略不是完美的,但实际用下来准确率在 94% 以上,够用了。
号码标记清除的实操经验
查到标记只是第一步,真正头疼的是手机号码标记清除。每个平台的清除流程都不一样,我简单列一下:
- 腾讯手机管家:有在线申诉入口,提交后 1-3 个工作日处理,成功率约 78%
- 华为号码:通过华为消费者服务热线或在线申诉,处理周期 3-5 个工作日
- 小米标记:在小米社区提交申诉,需要提供号码归属证明,处理较慢
- 360 手机卫士:有专门的号码申诉页面,提交后 2-5 个工作日
- 泰迪熊:需要发邮件到客服邮箱,处理周期最长,5-10 个工作日
- 电话邦:有在线申诉系统,但审核比较严格,需要营业执照
我的经验是:批量清除的时候,先清腾讯和华为的,因为这两个平台的数据会被其他小平台同步引用。腾讯和华为清干净了,过一两周你会发现有些小平台的标记也自动消失了。
号码标记 API 对接的注意事项
最后总结几个号码标记 API 对接的关键注意点:
第一,一定要做本地缓存。号码标记数据不是实时变化的,一个号码的标记状态一天内基本不会变。我们用 Redis 缓存查询结果,TTL 设 24 小时,缓存命中率 85% 以上,直接把 QPS 降了一个数量级。
第二,注意数据合规。号码标记数据涉及用户隐私,存储和展示都要符合《个人信息保护法》的要求。我们只存标记类型和标记来源,不存标记者的任何信息。展示的时候也只显示"该号码被标记为骚扰电话",不显示具体是谁标记的。
第三,做好降级预案。号码标记查询是增强型功能,不是核心功能。如果所有标记 API 都挂了,你的主业务不能受影响。我们的做法是标记查询失败时,直接跳过标记展示,不影响正常的业务流程。
第四,监控标记覆盖率的变化。我们每周会跑一次覆盖率统计,看看各平台的标记数据有没有异常下降。有一次发现电话邦的覆盖率突然从 64% 掉到 31%,排查后发现是他们的 API 返回格式偷偷改了,字段名从 markType 变成了 tagType。这种问题你不主动监控根本发现不了。
推荐资源
如果你正在做号码标记 API 对接,我推荐先去 Free API Hub 看看。上面整理了号码标记查询 API、骚扰电话标记查询、号码认证 API 等多个相关接口,每个都标注了接入难度、响应速度和文档质量。比你自己一个个去找要省事得多。
特别是对于刚起步的团队,前期没必要 9 个平台全接,可以先用 Free API Hub 上聚合好的号码标记查询 API,一个接口查多家数据,等业务量上来了再考虑自建聚合方案。电话标记接口这块,能少踩一个坑就少踩一个坑。
写在最后
号码标记这个领域,表面上看就是查一个号码有没有被标记,实际上背后的数据整合、冲突处理、稳定性保障,每一个都是坑。我做了三年号码认证相关的系统,到现在依然觉得这个领域没有银弹。
但有一点是确定的:号码标记查询正在从"可选功能"变成"必选功能"。随着运营商对骚扰电话的治理力度加大,以及用户对来电显示标记的依赖加深,不做号码标记查询的业务系统,迟早会出问题。
希望这篇文章能帮到正在做号码标记 API 对接的朋友。如果你有更好的方案或者踩过不同的坑,欢迎交流。毕竟这个领域,经验比文档值钱多了。