voidcaptcha
v0.1.0
Published
Official Node.js client for the VoidCaptcha CAPTCHA solving API: Turnstile, reCAPTCHA, Cloudflare Clearance, AWS WAF, DataDome and more.
Maintainers
Readme
voidcaptcha
Official Node.js client for VoidCaptcha, the CAPTCHA solving API. Cloudflare Turnstile and Clearance, reCAPTCHA v2 / v3 / Mobile, AWS WAF, DataDome, Imperva Incapsula, Arkose FunCaptcha, GeeTest and Hunt, through one small typed client.
npm install voidcaptchaNode 18+. TypeScript types included. Zero runtime dependencies (it uses the built-in fetch).
Works with import and require.
Quick start
import { VoidCaptcha } from 'voidcaptcha'
// const { VoidCaptcha } = require('voidcaptcha')
const client = new VoidCaptcha('void_your_api_key') // or set VOIDCAPTCHA_KEY
const result = await client.turnstile({
url: 'https://example.com/register',
siteKey: '0x4AAAAAAADnPIDROrmt1Wwj',
})
console.log(result.value) // the Turnstile token
console.log(result.userAgent) // the User-Agent the solve ran under
console.log(result.cost) // what it charged, e.g. "$0.0012"Get a key at voidcaptcha.com. Prices, supported sites and limits are on the website and in the docs; nothing in this package hard-codes them.
Every type
Each helper takes the documented fields in camelCase and returns a Solution.
result.value is the type's main answer; any other field of the API's solution object
is result.get('name') (or result.solution.name).
const proxy = 'proxy.example.com:9090:user:pass' // sticky session
// reCAPTCHA v2 (checkbox / invisible). variant: 'ReCaptchaV2Enterprise', 'ReCaptchaV2Max', ...
await client.recaptchaV2({ url: 'https://example.com/login', siteKey: '6LcR_okUAAAAAPYrPe-HK_0RULO1aZM15ENyM-Mf', proxy })
// reCAPTCHA v3. action must match what the page passes to grecaptcha.execute
await client.recaptchaV3({ url: 'https://example.com/login', siteKey: '6LdyC2cUAAAAACGuDKpXeDorzUDWXmdqeg-xy696', action: 'login', proxy })
// reCAPTCHA Mobile (Android). ReCaptchaMobileDirect needs no proxy
await client.recaptchaMobile({ siteKey: '6Lf...', appPackage: 'com.example.app', variant: 'ReCaptchaMobileDirect' })
// Cloudflare Clearance: replay the cookies AND the identity
const cf = await client.cfClearance({ url: 'https://example.com/', proxy })
const cookies = cf.get('cookies')
const headers = {
'user-agent': cf.get('user_agent'),
'sec-ch-ua': cf.get('sec_ch_ua'),
'sec-ch-ua-platform': cf.get('sec_ch_ua_platform'),
}
// AWS WAF: send result.value as the aws-waf-token cookie or x-aws-waf-token header
await client.awsWaf({ url: 'https://example.com/api/search', proxy })
// DataDome: result.value is the cookie, get('headers') the identity that earned it
await client.datadome({ url: 'https://example.com/search?q=shoes', proxy })
// Imperva Incapsula (reese84): incapsulaUrl is the reese84 script, not the page
await client.incapsula({ url: 'https://example.com/account', incapsulaUrl: 'https://example.com/Towardiant-Thou-Wals-Becommeth-Lady-Macb', proxy })
// Arkose FunCaptcha. variant: 'FunCaptcha_Invisible', 'FunCaptcha_Puzzle', 'FunCaptcha_Image'
await client.funcaptcha({ url: 'https://example.com/signup', siteKey: '476068BF-9607-4799-B53D-966BE98E2B81', jsDomain: 'client-api.arkoselabs.com', proxy })
// GeeTest v4 and v3. result.value is pass_token
await client.geetestV4({ url: 'https://example.com/register', siteKey: '54088bb07d2df3c46b79f80300b0abbe', proxy })
await client.geetestV3({ url: 'https://example.com/register', gt: '022397c99c9f646f6477822485f30404', challenge: 'fresh-challenge', proxy })
// Hunt Captcha: first call without data gives an X-HD, then send the site's meta.token
await client.hunt({ url: 'https://example.com/en/registration', apiGetLib: 'https://example.com/hd-api/external/apps/<app-hash>/api.js', proxy })Fields a helper does not name go in extra and are sent verbatim. For a type or option
without a helper, send the API's own task object:
await client.solve({ type: 'ReCaptchaV3Fast', url: '...', site_key: '...', proxy })Waiting: one call, or create and poll
By default each solve is one call to /v1/getResult that holds open until the answer is
ready. If your platform cannot hold a connection that long (serverless, strict proxies), poll:
const client = new VoidCaptcha({ poll: true }) // every solve via createTask + checkTask
// or drive it yourself
const taskId = await client.createTask({ type: 'Turnstile', url, site_key: siteKey })
const result = await client.waitForResult(taskId) // polls every 2 s, the API's minimumcheckTask(taskId) looks once and resolves null while the task is processing. Results are
held for ten minutes; after that it throws TaskNotFoundError.
Every method accepts { signal } (an AbortSignal) as its last argument to stop waiting.
Errors
Every API failure throws a subclass of VoidCaptchaError with the API's code, errorId,
description and httpStatus, plus retryable:
| Class | Codes | Retry? |
|---|---|---|
| AuthenticationError | 100-range: bad, missing or suspended key | no |
| InvalidRequestError (TaskNotFoundError) | 200-range: the request is wrong | no |
| SolveError | 300-range: blocked IP, unsolvable, timeout. Not charged | yes |
| BillingError (InsufficientBalanceError, ThreadLimitError, RateLimitError) | 400-range | balance: no; limits: after a pause |
| ServerError | 500-range, or a non-JSON answer | after a pause |
| NetworkError | no answer at all | after a pause, but see below |
| SolveTimeoutError | waitForResult gave up | check the task again |
import { SolveError } from 'voidcaptcha'
for (let attempt = 0; attempt < 3; attempt++) {
try {
return await client.recaptchaV2({ url, siteKey, proxy })
} catch (e) {
if (!(e instanceof SolveError)) throw e
}
}Solves are not idempotent. A NetworkError on the synchronous call can mean the solve
finished and was charged after your side gave up; use softId to correlate.
The full code list is at voidcaptcha.com/docs/errors.
Balance and test keys
console.log(await client.balance()) // 12.34 (US dollars)A test key (created in the dashboard) runs the same code path and returns a correctly
shaped but synthetic token, charging nothing. result.testMode is true for those.
Options
new VoidCaptcha({
apiKey: undefined, // default: process.env.VOIDCAPTCHA_KEY
timeoutMs: 210_000, // one solve, end to end
poll: false, // true: createTask + checkTask instead of one held call
pollIntervalMs: 2000, // never below 2000
requestTimeoutMs: 30_000, // per HTTP call while polling
softId: undefined, // label echoed back as softId
fetch: undefined, // your own fetch (e.g. undici with a ProxyAgent to reach the API)
})Links
- Docs: https://voidcaptcha.com/docs
- API reference: https://voidcaptcha.com/docs/api-reference
- Python client: https://pypi.org/project/voidcaptcha/
- Support: https://t.me/VoidCaptchaSupport
MIT licensed.
