Package Exports
- @onecaptcha/sdk
Readme
@onecaptcha/sdk
Official Node.js SDK for the OneCaptcha unified captcha-solving gateway.
- Zero dependencies. Built-in
fetch(Node 18+). - ESM only.
- Mirrors
project_spec/openapi.yamlexactly — same field names, same error codes.
Install
npm install @onecaptcha/sdkQuick start
import { OneCaptchaClient } from '@onecaptcha/sdk';
const client = new OneCaptchaClient({
apiKey: process.env.ONECAPTCHA_API_KEY,
baseUrl: 'https://api.one-captcha.com', // optional — this is the default
});
// Sync solve — gateway holds the connection until done.
const result = await client.solveRecaptchaV2({
url: 'https://example.com/login',
sitekey: '6Lc...',
});
console.log(result.token); // the gRecaptchaResponse token
console.log(result.cost); // 0.003887
console.log(result.balanceRemaining); // 0.997113Constructor options
| Option | Default | Description |
|---|---|---|
apiKey |
required | Your gateway API key (sent as X-API-Key) |
baseUrl |
https://api.one-captcha.com |
Gateway origin, no trailing slash |
timeout |
30000 |
ms timeout for non-solve calls |
solveTimeout |
120 (seconds) |
Default X-Timeout for sync solve(). Min 10, max 180. |
pollInterval |
5000 |
ms between polls in waitForTask |
pollTimeout |
180000 |
Total ms waitForTask waits before giving up |
fetch |
globalThis.fetch |
Custom fetch implementation (test injection) |
userAgent |
onecaptcha-sdk-node/0.1.0 |
Sent as User-Agent |
API
Sync solve
const result = await client.solve({
type: 'recaptcha_v2',
params: { url, sitekey },
idempotencyKey: '...optional UUID...', // safe retries within 5 minutes
timeout: 60, // optional per-call seconds
});Result fields (depend on captcha type):
type,status: 'ready'token— token-based widgets (reCAPTCHA, Turnstile, FunCaptcha, …)text— image OCRcoordinates— image-to-coordinatescookies— Amazon WAF / DataDomeuserAgent— when the provider returns onecost— USD charged to your balance for this callbalanceRemaining— your account balance after the operationrateLimit—{ limit, remaining, reset }
Per-type helpers
One per captcha type — sugar around solve():
client.solveImageToText({ body })
client.solveImageToCoordinates({ body, mode: 'points' })
client.solveRecaptchaV2({ url, sitekey, invisible: false, dataSValue: '...' })
client.solveRecaptchaV2Enterprise({ url, sitekey, enterprisePayload: { s: '...' } })
client.solveRecaptchaV3({ url, sitekey, action: 'login', minScore: 0.7 })
client.solveRecaptchaV3Enterprise({ url, sitekey, action: 'login', minScore: 0.7 })
client.solveFunCaptcha({ url, publicKey, subdomain, data })
client.solveGeeTestV3({ url, gt, challenge, apiServer })
client.solveGeeTestV4({ url, captchaId, apiServer })
client.solveTurnstile({ url, sitekey, action, cData })
client.solveAmazonWaf({ url, key, iv, context, challengeScript, captchaScript })
client.solveMtCaptcha({ url, sitekey })
client.solveProsopo({ url, sitekey })
client.solveFriendlyCaptcha({ url, sitekey })All accept an optional second argument with { idempotencyKey, timeout }.
Async submit + poll
// One call: submit + poll until ready.
const result = await client.solveAsync({
type: 'turnstile',
params: { url, sitekey },
});
// Or do it manually:
const submitted = await client.createTask({ type, params });
const result = await client.waitForTask(submitted.taskRef, {
pollInterval: 5_000,
pollTimeout: 180_000,
});
// Single non-blocking poll:
const r = await client.getTask(submitted.taskRef);
if (r.status === 'ready') console.log(r.token);taskRef is a self-contained signed token — no server-side session. Refs
expire 5 minutes after creation, and the signature binds them to your API
key, so they cannot be polled by another user.
Balance & feedback
const { balance, currency } = await client.getBalance();
await client.reportTask(taskRef, true); // target accepted the token
await client.reportTask(taskRef, false); // target rejected the tokenReport endpoints are best-effort: the gateway forwards them to providers that support feedback (reCAPTCHA family fully; image_to_text and FunCaptcha negative-only) and silently no-ops the rest.
Errors
Every public method throws OneCaptchaError on failure.
import { OneCaptchaError, ERROR_CODES } from '@onecaptcha/sdk';
try {
await client.solve({ type: 'recaptcha_v2', params: { url, sitekey } });
} catch (err) {
if (err instanceof OneCaptchaError) {
err.code // 'BALANCE_EMPTY' | 'PROVIDER_SITEKEY' | 'UNSOLVABLE' | …
err.message // human-readable explanation
err.status // HTTP status the gateway returned
err.field // for INPUT_* errors
err.retryable // boolean
err.retryAfter // seconds to wait
err.balanceRemaining // null or number
err.rateLimit // null or { limit, remaining, reset }
err.raw // raw error body from the gateway
}
}ERROR_CODES enumerates the codes, plus two SDK-only codes
(SDK_TIMEOUT, SDK_NETWORK) for local request failures.
Billing model
The gateway uses pre-charge with selective refund: the per-captcha-type
base price is debited from your balance before the provider call. On a
successful solve the charge stays. On a refund-safe failure (auth, balance,
input, sitekey/domain rejection, network errors, etc.) it is automatically
credited back. Errors that may have consumed sub-provider worker time
(UNSOLVABLE, real PROVIDER_TIMEOUT, …) are held to cover the upstream
cost.
The post-operation balance is on every billing-relevant response as
result.balanceRemaining (and on errors as err.balanceRemaining).
License
MIT