npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

voidcaptcha

v0.1.0

Published

Official Node.js client for the VoidCaptcha CAPTCHA solving API: Turnstile, reCAPTCHA, Cloudflare Clearance, AWS WAF, DataDome and more.

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 voidcaptcha

Node 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 minimum

checkTask(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.