Skip to content

Node.js SDK

Verified v2.0.0· 2026-05-28

Official CaptchaSonic library for Node.js and TypeScript — modern, Promise-based, multi-transport (gRPC / ConnectRPC / HTTP).

The CaptchaSonic Node.js SDK (captchasonic) is a modern, Promise-based, TypeScript-first library for solving CAPTCHAs at scale. Every method returns a Promise, ships full type definitions, and runs over three interchangeable transports — native gRPC (fastest, Node.js only), ConnectRPC (works in Node.js and browsers), and plain HTTP/JSON. Transient errors are retried automatically with exponential backoff.

It works in Node.js ≥ 18 and modern browsers, and is compatible with Express, Fastify, Next.js, React, Vue, and Vite.


Installation

npm install captchasonic
# or
yarn add captchasonic
# or
pnpm add captchasonic

Requirements

RequirementNotes
Node.js≥ 18.0.0 (required for the grpc transport)
Module formatES Module — the package ships "type": "module"
BrowserAny modern browser with fetch; use the connect or http transport

The package is published as ESM only. In an ESM project ("type": "module" in your package.json, or a .mjs file) import it directly:

import { CaptchaSonic } from "captchasonic";

From a CommonJS file, load it with a dynamic import():

// CommonJS (.cjs / "type": "commonjs")
const { CaptchaSonic } = await import("captchasonic");

TIP

If you are on TypeScript, set "module": "NodeNext" (or "ESNext") and "moduleResolution": "NodeNext" in tsconfig.json so the bundled type definitions resolve correctly.


Authentication

Create an API key in the CaptchaSonic dashboard and pass it as the first argument to the constructor:

import { CaptchaSonic } from "captchasonic";

const solver = new CaptchaSonic("YOUR_API_KEY");

Never hard-code keys in source control. Read the key from the environment instead:

import { CaptchaSonic } from "captchasonic";

const solver = new CaptchaSonic(process.env.CAPTCHASONIC_API_KEY);

Quick Start

Each captcha type has a dedicated solve* helper that submits the task and resolves with the result. There is no separate polling step for image tasks — the SDK handles it for you.

solveRecaptchaV2Tokentoken
Verified v2.0.0· 2026-05-28

reCAPTCHA v2 (token)Solve a reCAPTCHA v2 token challenge via browser automation (async — polls until ready).

signaturesolveRecaptchaV2Token(…)returnssolution.token
import { CaptchaSonic } from "captchasonic";

const solver = new CaptchaSonic(process.env.CAPTCHASONIC_API_KEY);

const result = await solver.solveRecaptchaV2Token({
  websiteURL: "https://example.com/login",
  websiteKey: "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
});

console.log(result.solution.gRecaptchaResponse);
// 03AGdBq25SxXT...  ← drop into the g-recaptcha-response field

Returns: image methods resolve a result whose answer lives under typedSolution (e.g. typedSolution.grid.objects, the selected tile indices); token methods return the token in the solution map.

TIP

Token-based methods (Turnstile, Cloudflare, reCAPTCHA v2/v3 token, popular-captcha token) submit a browser-automation task and poll internally until a token is ready — by default up to 120 seconds.


Supported CAPTCHA Types

All image methods take a single typed options object. Images may be Uint8Array, Node Buffer, or a string file path (file paths are Node.js only).

reCAPTCHA v2 (image)

await solver.solveRecaptchaV2({
  images: tiles,                 // typically 9 tiles for a 3×3 grid
  question: "traffic lights",    // plain text, or a Google class code like "/m/015qff"
});
// → result.typedSolution.grid.objects  (number[] — selected tile indices)

PopularCaptcha (hCaptcha-style image)

await solver.solvePopularCaptcha({
  images: tiles,                 // 1–64 tiles
  question: "Click each image with a cat",
  questionType: "objectClassify", // "objectClassify" | "grid" | "objectClick" | "objectDrag"
  examples,                      // optional reference images for objectClick
  websiteURL: "https://example.com",
});
// objectClassify/grid → typedSolution.grid.objects
// objectClick        → typedSolution.click
// objectDrag         → typedSolution.drag

GeeTest

// nine-grid (question + images required)
await solver.solveGeetest({ type: "nine", question: "Select all bicycles", images: tiles });

// click / icon (question + images required)
await solver.solveGeetest({ type: "click", question: "the bear", images: tiles });

// slide puzzle (piece + background)
await solver.solveGeetest({ type: "slide", images: [piece], examples: [background] });

// swap puzzles
await solver.solveGeetest({ type: "match" });
await solver.solveGeetest({ type: "winlinze" });

Returns: grid/click types land in typedSolution.grid.objects (or typedSolution.click); slide returns typedSolution.slide.x.

Accepted type aliases:

CanonicalAliases
nine-grid"nine", "geetest_nine", "9"
click / icon"click", "geetest_click", "icon"
slide"slide", "geetest_slide"
match"match", "geetest_match"
winlinze"winlinze", "geetest_winlinze"

OCR / image-to-text

await solver.solveOcr({ images: [img] });                                  // general 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]
OptionTypeNotes
module"common" | "mtcaptcha" | "bls" | "morocco"Default "common"
numericbooleanDigits only (auto-set for "bls")
caseSensitivebooleanPreserve letter case
minLength / maxLengthnumberLength bounds

TikTok

await solver.solveTikTok({ type: "click", question: "Select the shape", images });
await solver.solveTikTok({ type: "whirl", question: "Rotate to match", images, examples }); // examples required
await solver.solveTikTok({ type: "slide", question: "Slide to fit", images, examples });    // examples required

Returns: click lands in typedSolution.click; whirl/slide return typedSolution.slide.x.

type accepts "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] });

Returns: grid lands in typedSolution.grid.objects; slide returns typedSolution.slide.x.

type accepts "grid"/"binance_grid" and "slide"/"binance_slide".

AWS WAF

solveAwsWaf takes positional arguments. The question is formatted as "type:category:target".

await solver.solveAwsWaf(tiles, "grid:vehicles:cars");

Returns: selected tile indices in typedSolution.grid.objects.

Slide image (local, no AI)

Detects the slide offset locally using contour detection. Accepts one combined image, or [background, piece].

const r = await solver.solveSlideImage({ images: ["slide_bg.png", "piece.png"] });
console.log(r.typedSolution?.slide?.x); // pixel offset, e.g. 142

Returns: the slide offset in pixels at typedSolution.slide.x.

Token methods (browser automation)

These submit a task and poll internally until a token is returned (up to the polling timeout, 120s by default).

solveTurnstiletoken
Verified v2.0.0· 2026-05-28

Cloudflare TurnstileSolve a Cloudflare Turnstile token challenge (async — polls until ready).

signaturesolveTurnstile(…)returnssolution.token
await solver.solveTurnstile({ websiteURL, websiteKey, proxy });          // proxy optional
await solver.solveRecaptchaV2Token({ websiteURL, websiteKey, proxy });   // proxy optional
await solver.solveRecaptchaV3Token({ websiteURL, websiteKey, proxy });   // proxy optional
await solver.solvePopularCaptchaToken({ websiteURL, websiteKey, proxy, metadata }); // proxy optional
await solver.solveCloudflare({ websiteURL, websiteKey, proxy });         // proxy REQUIRED

Returns: the token in the solution map of the response (for example result.solution.token or result.solution.gRecaptchaResponse, depending on the captcha).


TypeScript Usage

The SDK exports types for every option object and response. Import them with 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);

Exported types

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 is Uint8Array | Buffer | string.


Proxy Support

Token / browser-automation methods accept an optional proxy string in the form http://user:pass@host:port:

await solver.solveTurnstile({
  websiteURL: "https://example.com",
  websiteKey: "0x4AAAAAAA...",
  proxy: "http://user:[email protected]:8080",
});

WARNING

solveCloudflare always requires a proxy — the proxy field is mandatory for that method. Other token methods run proxyless when proxy is omitted.

For enterprise hCaptcha you can also pass metadata (rqdata, rqtoken, fingerprint) to solvePopularCaptchaToken.


Configuration

Pass an options object as the second constructor argument. Only the options below are supported.

const solver = new CaptchaSonic("YOUR_API_KEY", {
  transport: "connect",   // "grpc" (default) | "connect" | "http"
  timeout: 180_000,       // max poll wait in ms (alias: timeoutMs); per-call default 30000
  pollingInterval: 5_000, // polling frequency in ms (default 2000)
  baseUrl: "https://api.captchasonic.com", // override endpoint (alias: url)
});
OptionTypeDefaultDescription
transport"grpc" | "connect" | "http""grpc"Wire protocol (see table below)
timeout / timeoutMsnumber30000 per callRequest timeout; timeout also caps polling wait
pollingIntervalnumber2000How often token tasks are polled
url / baseUrlstringper-transportOverride the API endpoint

Transports

TransportEnvironmentsProtocol
grpcNode.js onlygRPC binary over HTTP/2 — lowest latency, sends images as raw binary
connectNode.js and browsersConnectRPC over fetch
httpNode.js and browsersPlain REST/JSON over fetch

TIP

In the browser use connect (recommended) or http. Native gRPC requires Node.js. With connect/http, images are auto-encoded to base64 before sending; with grpc they are sent as raw binary with zero overhead.


Error Handling

All API-level failures throw a SonicError, which extends the built-in Error. It carries a numeric errorId and sets name to the matching error 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);    // e.g. "InvalidApiKeyError"
    console.error(err.message);
  } else {
    throw err; // network / unexpected
  }
}
errors7 canonical `errorId` codes
idPython exceptionNode errorNameMeaningAction
0SonicErrorSonicErrorBase class for every SDK error — catches anything below.Inspect `error.errorId` (Node) / `err.error_id` (Python) and branch.
1InvalidApiKeyErrorInvalidApiKeyErrorAPI key is missing, malformed, or revoked.Verify the key in your dashboard.
2InsufficientBalanceErrorInsufficientBalanceErrorAccount balance can't cover the task.Add funds.
3DailyLimitExceededErrorDailyLimitExceededErrorDaily quota exhausted.Wait for the daily reset or upgrade your plan.
4MinuteLimitExceededErrorMinuteLimitExceededErrorPer-minute rate limit hit.Slow down requests / add exponential backoff.
5QuotaExceededErrorQuotaExceededErrorPlan quota exhausted.Upgrade your plan.
6PlanExpiredErrorPlanExpiredErrorSubscription has expired.Renew your subscription.

Every concrete row inherits from SonicError — catch it once to handle all SDK failures, or branch on errorId / exception subclass for granular control. Transient gRPC errors are retried automatically with exponential backoff (up to 3 attempts).

TIP

Transient gRPC errors are retried automatically with exponential backoff (up to 3 attempts), so you usually only need to handle the SonicError cases above.


Account Helpers

const balance = await solver.getBalance(); // → number (USD)

const health = await solver.healthCheck(); // → { healthy: boolean, version: string }

Low-level task methods are available if you need direct control over submission and polling:

const created = await solver.createTask(task);        // submit a Partial<Task>
const result  = await solver.getTaskResult(taskId);   // poll for a result

Troubleshooting

require is not defined / Cannot use import statement outside a module — the package is ESM only. Use import in an ESM project, or await import("captchasonic") from CommonJS. See Installation.

grpc transport fails in the browser — native gRPC is Node.js only. Switch to transport: "connect" or transport: "http".

InvalidApiKeyError on every call — confirm the key is passed as the first constructor argument and not accidentally undefined (e.g. a missing env var).

Token method times out — token tasks poll up to timeout ms (default 120000). Increase timeout and verify the websiteURL / websiteKey match the target page. For Cloudflare, a valid proxy is required.

File-path images don't load — string file paths are read synchronously and are Node.js only; in the browser pass a Uint8Array instead.


Resources