Node.js SDK
适用于 Node.js 和 TypeScript 的官方 CaptchaSonic 库 —— 现代化、基于 Promise、支持多种传输方式(gRPC / ConnectRPC / HTTP)。
CaptchaSonic Node.js SDK(captchasonic)是一个现代化、基于 Promise、TypeScript 优先的库,用于大规模求解 CAPTCHA。每个方法都返回 Promise,自带完整的类型定义,并可在三种可互换的传输方式上运行 —— 原生 gRPC(最快,仅限 Node.js)、ConnectRPC(可在 Node.js 和浏览器中运行)以及普通的 HTTP/JSON。瞬时错误会以指数退避方式自动重试。
可在 Node.js ≥ 18 和现代浏览器中运行,并兼容 Express、Fastify、Next.js、React、Vue 和 Vite。
安装
npm install captchasonic
# 或者
yarn add captchasonic
# 或者
pnpm add captchasonic
环境要求
| 要求 | 说明 |
|---|---|
| Node.js | ≥ 18.0.0(grpc 传输方式所必需) |
| 模块格式 | ES Module —— 该包发布时带有 "type": "module" |
| 浏览器 | 任何支持 fetch 的现代浏览器;请使用 connect 或 http 传输方式 |
该包仅以 ESM 形式发布。在 ESM 项目中(package.json 中包含 "type": "module",或使用 .mjs 文件)直接导入:
import { CaptchaSonic } from "captchasonic";
在 CommonJS 文件中,使用动态 import() 加载:
// CommonJS (.cjs / "type": "commonjs")
const { CaptchaSonic } = await import("captchasonic");
TIP
如果使用 TypeScript,请在 tsconfig.json 中设置 "module": "NodeNext"(或 "ESNext")和 "moduleResolution": "NodeNext",以便正确解析自带的类型定义。
身份验证
在 CaptchaSonic 控制台 创建 API 密钥,并将其作为构造函数的第一个参数传入:
import { CaptchaSonic } from "captchasonic";
const solver = new CaptchaSonic("YOUR_API_KEY");
切勿将密钥硬编码到版本控制中。请改为从环境变量读取密钥:
import { CaptchaSonic } from "captchasonic";
const solver = new CaptchaSonic(process.env.CAPTCHASONIC_API_KEY);
快速开始
每种验证码类型都有专门的 solve* 辅助方法,用于提交任务并返回结果。对于图像类任务,无需单独的轮询步骤 —— SDK 会自动处理。
import { CaptchaSonic } from "captchasonic";
const solver = new CaptchaSonic(process.env.CAPTCHASONIC_API_KEY);
const result = await solver.solveRecaptchaV2({
images: tiles, // Uint8Array[] | Buffer[] | file-path[]
question: "traffic lights",
});
// 网格选择以图块索引的形式返回
console.log(result.typedSolution?.grid?.objects); // 例如 [2, 4, 7]
返回值: 图像方法返回的结果中,答案位于
typedSolution下(此处为typedSolution.grid.objects,即选中的图块索引);令牌方法在solution映射中返回令牌。
TIP
基于令牌的方法(Turnstile、Cloudflare、reCAPTCHA v2/v3 令牌、popular-captcha 令牌)会提交一个浏览器自动化任务并在内部轮询,直到令牌就绪 —— 默认最长 120 秒。
支持的验证码类型
所有图像方法都接受单个类型化的选项对象。图像可以是 Uint8Array、Node Buffer 或字符串文件路径(文件路径仅限 Node.js)。
reCAPTCHA v2(图像)
await solver.solveRecaptchaV2({
images: tiles, // 3×3 网格通常为 9 个图块
question: "traffic lights", // 纯文本,或 Google 类别代码,如 "/m/015qff"
});
// → result.typedSolution.grid.objects (number[] —— 选中的图块索引)
PopularCaptcha(hCaptcha 风格图像)
await solver.solvePopularCaptcha({
images: tiles, // 1–64 个图块
question: "Click each image with a cat",
questionType: "objectClassify", // "objectClassify" | "grid" | "objectClick" | "objectDrag"
examples, // objectClick 的可选参考图像
websiteURL: "https://example.com",
});
// objectClassify/grid → typedSolution.grid.objects
// objectClick → typedSolution.click
// objectDrag → typedSolution.drag
GeeTest
// 九宫格(需要 question 和 images)
await solver.solveGeetest({ type: "nine", question: "Select all bicycles", images: tiles });
// click / icon(需要 question 和 images)
await solver.solveGeetest({ type: "click", question: "the bear", images: tiles });
// 滑块拼图(拼块 + 背景)
await solver.solveGeetest({ type: "slide", images: [piece], examples: [background] });
// 交换拼图
await solver.solveGeetest({ type: "match" });
await solver.solveGeetest({ type: "winlinze" });
返回值:grid/click 类型位于 typedSolution.grid.objects(或 typedSolution.click);slide 返回 typedSolution.slide.x。
可接受的 type 别名:
| 标准名称 | 别名 |
|---|---|
| 九宫格 | "nine"、"geetest_nine"、"9" |
| click / icon | "click"、"geetest_click"、"icon" |
| slide | "slide"、"geetest_slide" |
| match | "match"、"geetest_match" |
| winlinze | "winlinze"、"geetest_winlinze" |
OCR / 图像转文本
await solver.solveOcr({ images: [img] }); // 通用 OCR
await solver.solveOcr({ images: [img], module: "mtcaptcha", maxLength: 4 });
await solver.solveOcr({ images: imgs, module: "bls", numeric: true, maxLength: 3 });
// → result.typedSolution.text.texts[0]
| 选项 | 类型 | 说明 |
|---|---|---|
module | "common" | "mtcaptcha" | "bls" | "morocco" | 默认 "common" |
numeric | boolean | 仅数字("bls" 时自动启用) |
caseSensitive | boolean | 保留字母大小写 |
minLength / maxLength | number | 长度范围 |
TikTok
await solver.solveTikTok({ type: "click", question: "Select the shape", images });
await solver.solveTikTok({ type: "whirl", question: "Rotate to match", images, examples }); // 需要 examples
await solver.solveTikTok({ type: "slide", question: "Slide to fit", images, examples }); // 需要 examples
返回值:click 位于 typedSolution.click;whirl/slide 返回 typedSolution.slide.x。
type 接受 "click"/"tiktok_click"、"whirl"/"tiktok_whirl"、"slide"/"tiktok_slide"。
Binance
await solver.solveBinance({ type: "grid", question: "Select the bicycle", images });
await solver.solveBinance({ type: "slide", images: [puzzle], examples: [background] });
返回值:grid 位于 typedSolution.grid.objects;slide 返回 typedSolution.slide.x。
type 接受 "grid"/"binance_grid" 和 "slide"/"binance_slide"。
AWS WAF
solveAwsWaf 使用位置参数。question 的格式为 "type:category:target"。
await solver.solveAwsWaf(tiles, "grid:vehicles:cars");
返回值:选中的图块索引位于 typedSolution.grid.objects。
滑块图像(本地,无 AI)
使用轮廓检测在本地确定滑块偏移量。接受一张合并图像,或 [background, piece]。
const r = await solver.solveSlideImage({ images: ["slide_bg.png", "piece.png"] });
console.log(r.typedSolution?.slide?.x); // 像素偏移量,例如 142
返回值:滑块偏移量(像素)位于 typedSolution.slide.x。
令牌方法(浏览器自动化)
这些方法会提交任务并在内部轮询,直到返回令牌(最长至轮询超时时间,默认 120 秒)。
await solver.solveTurnstile({ websiteURL, websiteKey, proxy }); // proxy 可选
await solver.solveRecaptchaV2Token({ websiteURL, websiteKey, proxy }); // proxy 可选
await solver.solveRecaptchaV3Token({ websiteURL, websiteKey, proxy }); // proxy 可选
await solver.solvePopularCaptchaToken({ websiteURL, websiteKey, proxy, metadata }); // proxy 可选
await solver.solveCloudflare({ websiteURL, websiteKey, proxy }); // proxy 必填
返回值:令牌位于响应的 solution 映射中(例如 result.solution.token 或 result.solution.gRecaptchaResponse,取决于验证码类型)。
TypeScript 用法
SDK 为每个选项对象和响应导出类型。请使用 import type 导入它们。
import { CaptchaSonic } from "captchasonic";
import type { SolveGeetestOptions, GetTaskResultResponse } from "captchasonic";
const solver = new CaptchaSonic(process.env.CAPTCHASONIC_API_KEY!);
const opts: SolveGeetestOptions = {
type: "nine",
question: "Select all bicycles",
images: tiles,
};
const result = await solver.solveGeetest(opts);
console.log(result.typedSolution?.grid?.objects);
导出的类型
import type {
CaptchaSonicOptions,
ImageInput,
SolvePopularCaptchaOptions,
SolveRecaptchaV2Options,
SolveGeetestOptions,
SolveOcrOptions,
SolveTikTokOptions,
SolveBinanceOptions,
SolveTurnstileOptions,
SolvePopularCaptchaTokenOptions,
SolveRecaptchaV2TokenOptions,
SolveRecaptchaV3TokenOptions,
SolveCloudflareOptions,
SolveSlideImageOptions,
GeetestSubtype,
TikTokSubtype,
BinanceSubtype,
Task,
CreateTaskResponse,
GetTaskResultResponse,
} from "captchasonic";
ImageInput 即 Uint8Array | Buffer | string。
代理支持
令牌 / 浏览器自动化方法接受可选的 proxy 字符串,格式为 http://user:pass@host:port:
await solver.solveTurnstile({
websiteURL: "https://example.com",
websiteKey: "0x4AAAAAAA...",
proxy: "http://user:[email protected]:8080",
});
WARNING
solveCloudflare 始终需要代理 —— 该方法的 proxy 字段为必填项。其他令牌方法在省略 proxy 时以无代理方式运行。
对于企业版 hCaptcha,你还可以向 solvePopularCaptchaToken 传递 metadata(rqdata、rqtoken、fingerprint)。
配置
将选项对象作为构造函数的第二个参数传入。仅支持以下选项。
const solver = new CaptchaSonic("YOUR_API_KEY", {
transport: "connect", // "grpc"(默认) | "connect" | "http"
timeout: 180_000, // 最大轮询等待时间(毫秒,别名:timeoutMs);单次调用默认 30000
pollingInterval: 5_000, // 轮询频率(毫秒,默认 2000)
baseUrl: "https://api.captchasonic.com", // 覆盖端点(别名:url)
});
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
transport | "grpc" | "connect" | "http" | "grpc" | 传输协议(见下表) |
timeout / timeoutMs | number | 单次调用 30000 | 请求超时;timeout 同时限制轮询等待时间 |
pollingInterval | number | 2000 | 令牌任务的轮询频率 |
url / baseUrl | string | 因传输方式而异 | 覆盖 API 端点 |
传输方式
| 传输方式 | 运行环境 | 协议 |
|---|---|---|
grpc | 仅 Node.js | 基于 HTTP/2 的 gRPC 二进制 —— 延迟最低,以原始二进制发送图像 |
connect | Node.js 和浏览器 | 基于 fetch 的 ConnectRPC |
http | Node.js 和浏览器 | 基于 fetch 的普通 REST/JSON |
TIP
在浏览器中请使用 connect(推荐)或 http。原生 gRPC 需要 Node.js。使用 connect/http 时,图像在发送前会自动编码为 base64;使用 grpc 时,图像以原始二进制发送,无额外开销。
错误处理
所有 API 级别的失败都会抛出 SonicError,它继承自内置的 Error。它带有数字 errorId,并将 name 设置为相应的错误名称。
import { CaptchaSonic, SonicError } from "captchasonic";
try {
const result = await solver.solveGeetest({ type: "nine", question: "bicycles", images });
} catch (err) {
if (err instanceof SonicError) {
console.error(err.errorId); // 1–6
console.error(err.name); // 例如 "InvalidApiKeyError"
console.error(err.message);
} else {
throw err; // 网络 / 意外错误
}
}
errorId | name | 原因 | 处理方式 |
|---|---|---|---|
| 1 | InvalidApiKeyError | API 密钥缺失或无效 | 检查控制台中的密钥 |
| 2 | InsufficientBalanceError | 额度不足 | 充值余额 |
| 3 | DailyLimitExceededError | 超出每日配额 | 等待每日重置或提升套餐 |
| 4 | MinuteLimitExceededError | 触发每分钟速率限制 | 退避后稍后重试 |
| 5 | QuotaExceededError | 套餐配额耗尽 | 升级套餐 |
| 6 | PlanExpiredError | 订阅已过期 | 续订订阅 |
TIP
瞬时 gRPC 错误会以指数退避方式自动重试(最多 3 次),因此通常只需处理上面列出的 SonicError 情况。
账户辅助方法
const balance = await solver.getBalance(); // → number (USD)
const health = await solver.healthCheck(); // → { healthy: boolean, version: string }
如果需要直接控制任务提交和轮询,可使用底层任务方法:
const created = await solver.createTask(task); // 提交 Partial<Task>
const result = await solver.getTaskResult(taskId); // 轮询结果
故障排查
require is not defined / Cannot use import statement outside a module —— 该包仅为 ESM。请在 ESM 项目中使用 import,或在 CommonJS 中使用 await import("captchasonic")。参见安装。
grpc 传输方式在浏览器中失败 —— 原生 gRPC 仅限 Node.js。请切换到 transport: "connect" 或 transport: "http"。
每次调用都返回 InvalidApiKeyError —— 确认密钥作为构造函数的第一个参数传入,且没有意外为 undefined(例如环境变量缺失)。
令牌方法超时 —— 令牌任务最长轮询 timeout 毫秒(默认 120000)。请增大 timeout,并核实 websiteURL / websiteKey 与目标页面匹配。对于 Cloudflare,需提供有效的 proxy。
文件路径图像无法加载 —— 文件路径以同步方式读取,仅限 Node.js;在浏览器中请改为传入 Uint8Array。