验证码API轮询和回调有什么区别:流程、延迟、部署条件与怎么选

2026-10-01 56

两种方式拿到的是同一个识别结果,区别在于由谁通知谁。

  • 轮询:你的程序提交任务后,每隔几秒问服务端一次“好了没有”。
  • 回调:你在提交任务时留下一个公网地址,服务端算完后把结果主动 POST 过来。

下面几类环境用轮询最省事:本地脚本、单机运行的 Playwright/Selenium、没有公网入口的内网环境。

下面几类场景用回调更合适:分布式采集、由消息队列驱动的后端、并发很高的任务池。回调能减少空请求,结果到达也更及时。代价是你要自己维护一个可靠的接收端,而且最好保留轮询作为兜底。

两种方式的流程

验证码识别 API 大多采用异步任务模型。你先调用 createTask 提交任务,拿到一个 taskId,识别过程在服务端进行。reCAPTCHA、Cloudflare Turnstile、GeeTest 这类返回 token 的任务,通常要几秒到几十秒才能出结果。轮询和回调的差别,只在于结果怎么回到你手里。

轮询与回调的请求时序对比,包括回调超时后的轮询兜底

轮询(拉取)

  1. 调用 createTask,拿到 taskId。
  2. 按固定间隔或逐步拉长的间隔调用 getTaskResult。
  3. 状态还是“处理中”就继续等;变成“完成”时取出 token 或坐标;返回错误时进入异常处理。

回调(推送)

  1. 在 createTask 请求中带上回调地址。有的平台还要求先在后台登记接收域名,例如 2Captcha 的 pingback 机制。
  2. 任务完成后,平台向这个地址发起 HTTP 请求,把结果推送过来。
  3. 你的接收端解析结果,再交给后续业务处理。

五个关键差异

维度轮询回调
网络要求只需能访问外网需要公网可达的 HTTPS 接收端
结果感知延迟额外受轮询间隔影响完成后即推送
请求次数处理中会有多次空查询完成时一次推送
代码结构在同一流程里线性等待提交和接收分离,需要关联 ID
失败风险主要是超时和限流推送丢失、接收端宕机或响应慢

1. 网络条件决定了能不能用回调

轮询只需要出站访问。程序在 NAT 后面、办公电脑上、没有固定公网 IP 的机器上都能运行。

回调则要求你对外暴露一个可路由、稳定在线的接收地址,通常还要配置 HTTPS 证书、防火墙放行和入站防护。如果你的自动化跑在笔记本或内网 CI 里,单为回调去搭一个公网入口并不划算。

2. 延迟:轮询间隔会直接加到等待时间上

回调在任务完成时就发出通知。轮询要等到下一次查询才能发现结果已经好了。

粗略估算:假设轮询间隔为 T,结果完成的时刻在两次查询之间均匀分布,那么平均多等约 T/2,最坏多等约 T。以 5 秒间隔为例,平均多等约 2.5 秒,最坏接近 5 秒。这是按假设推出的估算,不是实测值。

对单个任务来说,这点差距往往不明显。但如果链路里有多次验证码,或者 token 有效期较短(reCAPTCHA 等 token 的有效期一般以分钟计),这段额外等待就会挤占后续操作的时间。

3. 请求量:轮询的空查询会累积

一个任务处理 20 秒、每 3 秒查一次,就会产生六七次没有结果的请求。并发上千个任务时,这些空查询会明显增加你的出站连接数,也会给服务端带来压力。查询过密还可能触发服务商的限流。

回调把多次查询压缩成一次推送。量越大,这一点越有价值。

4. 代码结构:回调需要自己做结果对应

用轮询时,Playwright 或 Puppeteer 脚本可以在同一个函数里提交任务、等待结果,然后立刻把 token 注入页面。浏览器会话、页面状态和结果天然在一起。

用回调时,提交任务和接收结果发生在两个不同的服务里。你需要用 taskId(或自己的关联 ID)把推送回来的结果对应到正确的任务上,比如:

  • 唤醒挂起的浏览器会话;
  • 更新数据库里的那条记录;
  • 把结果投递给下游队列。

对应错了,就会出现“token 注入到另一个页面”这类很难排查的问题。

5. 失败处理:回调不是一次就能保证送达

推送可能因为网络抖动、接收端宕机或响应太慢而失败。部分平台对接收端的响应时限要求很严,例如 CapMonster Cloud 文档要求接收端在 2 秒内响应。

轮询的失败面相对简单:主要是总超时和限流,处理逻辑都在你自己的进程里。

怎么选

适合轮询的情况

  • 本地或单机脚本。
  • 用浏览器自动化做自有系统、或已获授权系统的测试。
  • 并发不高的场景。
  • 没有公网入口的环境。

CI 里的回归测试也通常用轮询。另外,如果你测的是自有站点,先看验证码厂商是否提供官方测试密钥,能用测试密钥就不必调用识别服务,可参考 Playwright 验证码测试怎么做。

适合回调的情况

  • 无状态后端或多 worker 的分布式采集。
  • 由队列驱动的流水线。
  • 并发高、对结果感知延迟敏感的业务。

生产环境的稳妥做法:回调为主,轮询兜底

平时靠回调拿结果。如果某个任务超过预期时长仍未收到推送,就主动调用 getTaskResult 查一次状态,避免任务永久挂起。这次查询也能顺便用来对账。

轮询的通用写法

以下为通用伪代码。字段名和状态值以所用服务的文档为准。

task_id = createTask(任务参数)
sleep(初始等待)            # token 类任务很少在一两秒内完成
deadline = now() + 总超时
while now() < deadline:
    r = getTaskResult(task_id)
    if r 表示完成:   return r 中的结果
    if r 表示错误:   按错误类型重试或放弃
    sleep(间隔)             # 常见 3~5 秒,可逐步拉长
记录 task_id,按超时处理

有三点要注意:

  • 间隔不要过密:间隔太短,空查询和限流风险都会上升。
  • 必须设置总超时:否则任务异常时,循环会一直跑下去。
  • 记录 taskId:出问题时,你可以拿着它和服务商核对。

完整的 Python 接入示例可参考 Python 验证码识别 API 接入。

回调接收端要做的几件事

以下同样是通用伪代码。具体的请求格式和校验方式以服务商文档为准。

POST /captcha-callback
  校验来源(签名、共享密钥参数或来源 IP,取决于服务商)
  解析 taskId 和结果
  若该 taskId 已处理过:直接返回 200(保证幂等)
  写入存储或投递到队列
  立即返回 200,不在这里执行耗时业务

为什么要幂等:平台在重试推送时,同一条结果可能到达不止一次。

为什么要立刻返回:把注入 token、提交表单这类慢操作放进接收端,很容易超过平台的响应时限,平台会认为推送失败。

最后还要记住,接收端收到结果,只说明识别结果已经返回。目标站点是否接受这个结果、业务是否真正完成,需要在下游另外判断和统计。

在 NexCaptcha 上怎么用

NexCaptcha 官网介绍的是统一的任务模式:

  • 接口:createTask、getTaskResult、getBalance;
  • 接口域名:api.nexcaptcha.net;
  • 密钥:放在请求体的 clientKey 字段;
  • 取结果方式:轮询和 webhook 两种。

用轮询时,按上面的循环写即可。打算用回调时,下面几项要在接入前通过官方文档或工程师确认,再据此编写接收端:

  • 推送请求体的格式;
  • 是否带签名,以及如何校验;
  • 推送失败后的重试次数和间隔;
  • 对接收端的响应时限要求。

这些信息和支持的验证码类型,可在 NexCaptcha 官网 查看或联系工程师确认。

相关文章

Cloudflare Turnstile识别实战:Siteverify强制校验、Pre-Clearance通行证与会话一致性架构
公开价格监控验证码处理实战:多平台比价采集的拦截分流、会话对齐与高并发调度
2Captcha替代方案怎么选:从众包延迟到WebSocket推送与Adapter平滑迁移实战
Python 验证码识别 API 怎么接:异步 httpx 封装、WebSocket 推送与 Playwright 联调实战
验证码识别自建还是选打码平台?从开源 OCR、大模型调用到企业级 API 的架构选型实战
超级鹰替代方案怎么选:从截图传图到Token直解的架构演进与迁移实战