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

capskip

v1.3.0

Published

Captcha solver for Node.js: solve reCAPTCHA v2/v3, Cloudflare Turnstile, GeeTest and image captchas with CapSkip, a local unlimited captcha solver with no per-solve fees.

Readme

CapSkip Node.js SDK — Captcha Solver for Node.js

npm Node.js 18+ License: MIT Tests

Solve reCAPTCHA v2, reCAPTCHA v3, Cloudflare Turnstile, GeeTest, ALTCHA, Capy Puzzle, CaptchaFox, Friendly Captcha and image captchas from Node.js.

Official Node.js client for CapSkip, a local captcha solver that runs on your own machine. Licensed once, not billed per solve. TypeScript definitions included.

npm install capskip

How it works

CapSkip is a desktop app. It does the solving on your machine and exposes the standard captcha-solver HTTP API — the same in.php / res.php endpoints every 2captcha-compatible client already speaks — on 127.0.0.1:8080.

This SDK is a thin wrapper over that API, with the method names you would expect: normal(), recaptcha(), turnstile(), geetest(), altcha(), capy(), captchafox(), friendlyCaptcha(). Nothing leaves your network, and there is no credit balance to keep an eye on.

Supported captcha types

| Captcha | Method | |---|---| | Image captcha solver (distorted text / OCR) | solver.normal(file) | | reCAPTCHA v2 solver (checkbox) | solver.recaptcha(sitekey, url) | | reCAPTCHA v2 invisible solver | solver.recaptcha(sitekey, url, { invisible: 1 }) | | reCAPTCHA Enterprise solver | solver.recaptcha(sitekey, url, { enterprise: 1 }) | | reCAPTCHA v3 solver | solver.recaptcha(sitekey, url, { version: 'v3', action: 'submit' }) | | reCAPTCHA v3 Enterprise | solver.recaptcha(sitekey, url, { version: 'v3', enterprise: 1 }) | | Cloudflare Turnstile solver (widget) | solver.turnstile(sitekey, url) | | Cloudflare Turnstile (challenge page) | solver.turnstile(sitekey, url, { data, pagedata }) | | GeeTest v3 solver (slide puzzle) | solver.geetest(gt, challenge, url) | | ALTCHA solver (proof-of-work) | solver.altcha(url, { challengeUrl }) | | Capy Puzzle solver (slide puzzle) | solver.capy(sitekey, url) | | CaptchaFox solver (widget) | solver.captchafox(sitekey, url) | | Friendly Captcha solver (proof-of-work) | solver.friendlyCaptcha(sitekey, url, { version }) |

hCaptcha and FunCaptcha/Arkose are not supported. hCaptcha is the one people misidentify most often, since it also puts a data-sitekey on the widget — check for class="h-captcha" or a js.hcaptcha.com script before reaching for recaptcha().

Point the SDK at the live captcha demo pages to sanity-check your setup against real widgets.


Quick start (5 minutes)

1. Install the CapSkip captcha solver

Download and run the CapSkip desktop app — CapSkipInstaller.msi. Leave it running in the background.

In CapSkip settings, note:

  • API port (default: 8080)
  • API key (optional — if validation is disabled, any string works)

2. Install the SDK

npm install capskip

Or from source:

git clone https://github.com/capskip/capskip-node.git
cd capskip-node
npm install

3. Solve your first captcha

const { CapSkip } = require('capskip');

const solver = new CapSkip({ host: '127.0.0.1', port: 8080 });

(async () => {
  const result = await solver.recaptcha(
    'YOUR_SITEKEY',
    'https://example.com/page-with-recaptcha',
  );

  console.log(result.code); // g-recaptcha-response token
})();

Prerequisite: CapSkip must be running before you call the SDK. If you see a connection error, see Troubleshooting.

Every solve method returns a Promise — use await or .then().


Why solve captchas locally

Cloud captcha APIs charge per solve, which turns a retry loop into an expense and routes every page URL and sitekey you touch through someone else's queue.

CapSkip flips that around:

  • Unlimited solving — one license, no per-captcha charge, no balance to top up
  • Runs on 127.0.0.1 — the SDK never talks to a third-party server
  • No per-key rate limit — throughput is whatever your machine can manage
  • Fast — image captchas come back in well under a second; a typical reCAPTCHA v2 lands in 30–45 seconds

Coming from 2captcha or Anti-Captcha

CapSkip answers on the same in.php / res.php endpoints, so it works as a 2captcha API alternative: an existing integration usually needs nothing more than its host pointed at 127.0.0.1:8080. The migration notes cover the details, if you would rather keep your current client library than switch to this one.


Documentation

| Guide | Description | |---|---| | Tutorial | Complete walkthrough of every captcha type | | Getting Started | Full setup: CapSkip app, SDK install, first script | | API Reference | All classes, methods, parameters, and return values | | Examples | Ready-to-run scripts for every captcha type | | Troubleshooting | Connection errors, timeouts, proxy issues | | Contributing | Development setup, tests, pull requests | | Changelog | Release history |


Configuration

const { CapSkip } = require('capskip');

const solver = new CapSkip({
  apiKey: 'capskip',        // your CapSkip API key (or any string if validation is off)
  host: '127.0.0.1',        // CapSkip host
  port: 8080,               // CapSkip port from app settings
  defaultTimeout: 120,      // seconds — image captcha polling timeout
  recaptchaTimeout: 300,    // seconds — reCAPTCHA / Turnstile / GeeTest / CaptchaFox /
                            // Friendly Captcha polling timeout
                            // (ALTCHA and Capy use defaultTimeout — neither is a browser solve)
  pollingInterval: 5,       // max seconds between res.php polls (starts at 0.25s, backs off to this)
});

Use environment variables in production:

# Linux / macOS
export CAPSKIP_API_KEY="your-key"
export CAPSKIP_HOST="127.0.0.1"
export CAPSKIP_PORT="8080"
# Windows PowerShell
$env:CAPSKIP_API_KEY = "your-key"
$env:CAPSKIP_HOST = "127.0.0.1"
$env:CAPSKIP_PORT = "8080"
const { CapSkip } = require('capskip');

const solver = new CapSkip({
  apiKey: process.env.CAPSKIP_API_KEY || 'capskip',
  host: process.env.CAPSKIP_HOST || '127.0.0.1',
  port: parseInt(process.env.CAPSKIP_PORT || '8080', 10),
});

Usage examples

Image captcha

await solver.normal('captcha.png');
await solver.normal('https://example.com/captcha.jpg');
await solver.normal('data:image/png;base64,iVBORw0KGgo...');
// result.code holds the recognized text

reCAPTCHA v2 / v3

// reCAPTCHA v2
const v2 = await solver.recaptcha('...', 'https://example.com');

// reCAPTCHA v3
const v3 = await solver.recaptcha('...', 'https://example.com', {
  version: 'v3',
  action: 'submit',
  score: 0.7,
});

Cloudflare Turnstile

const result = await solver.turnstile('0x4AAAAAAA...', 'https://example.com');

GeeTest v3

gt is static per site, but challenge is single-use and expires in about a minute — fetch a fresh pair right before solving.

const result = await solver.geetest(
  '81388ea1fc187e0c335c0a8907ff2625',
  '7cf6a8b1a2c34d5e6f7089abcdef0123',
  'https://example.com/login',
);

// Post these back exactly as the site's own front-end would
result.challenge, result.validate, result.seccode;

ALTCHA

ALTCHA is proof-of-work, not recognition — there is nothing to read, so a solve is deterministic and takes milliseconds. Give CapSkip the endpoint that serves the challenge, or the challenge document itself.

const result = await solver.altcha('https://example.com/signup', {
  challengeUrl: 'https://example.com/captcha/api/altcha/challenge',
});

// Post this back in the form field the widget uses, named `altcha`
result.token;

Challenges expire fast — some sites inside two minutes — so fetch one immediately before solving and submit the token promptly.

Capy Puzzle

A Capy solution is not a token. It is three values that together go into the target form, so post all three back exactly as returned.

const result = await solver.capy(
  'PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v',
  'https://example.com/login',
);

// capy_captchakey, capy_challengekey and capy_answer respectively
result.captchakey, result.challengekey, result.answer;

answer is the drag path the widget would have recorded — submit it verbatim, without trimming or re-encoding it. The challenge key is single-use and short-lived, so submit promptly rather than caching the three values.

Pass api_server when the widget script points somewhere other than https://jp.api.capy.me. Only the puzzle family is solved; version: 'avatar' is refused rather than answered with a puzzle answer the site would reject.

CaptchaFox

const result = await solver.captchafox(
  'sk_xtNxpk6fCdFbxh1_xJeGflSdCE9tn99G',
  'https://example.com/signup',
);

// Post this back in the form field the widget uses, named `cf-captcha-response`
result.token;

// The UA the token was minted under — submit under this one, not your own
result.userAgent;

url has to be the page the widget actually runs on: CaptchaFox checks it against the domains the key is registered for and refuses a mismatch permanently, not intermittently.

Pass api_server only when the page does not load the default widget. A page loading the MAM package expects a MAM_ prefixed token, and sending the wrong source still succeeds — it just returns a token the site will not accept, which reads as a silent verification failure rather than an error.

Friendly Captcha

Two different protocols ship under this name and a sitekey does not tell you which one a site uses, so say which — or send the widget script and let CapSkip read it off the build the site actually loads.

const result = await solver.friendlyCaptcha(
  'FCMGEMUD2M567T8G',
  'https://example.com/signup',
  { version: 'v2' },
);

// v1 → frc-captcha-solution, v2 → frc-captcha-response
result.token;
// Or let the script URL decide the version
const result = await solver.friendlyCaptcha(
  'FCMGEMUD2M567T8G',
  'https://example.com/signup',
  { moduleScript: 'https://cdn.example.com/@friendlycaptcha/[email protected]/site.min.js' },
);

Solving the wrong version returns a well-formed token the target site rejects, with nothing to indicate the version was the problem. With neither version nor a script URL, v1 is assumed. Pass api_server: 'eu' for a sitekey on the EU data-residency tenant. A v2 token is roughly six kilobytes, so size whatever carries it accordingly.

With a proxy (every method except image captcha)

// Proxy is not supported for image captcha
await solver.recaptcha('...', 'https://example.com', {
  proxy: { type: 'HTTPS', uri: 'user:[email protected]:3128' },
});
await solver.turnstile('...', 'https://example.com', {
  proxy: { type: 'HTTP', uri: '1.2.3.4:3128' },
});

Parallel solving

const { CapSkip } = require('capskip');

async function main() {
  const solver = new CapSkip();
  const [r1, r2] = await Promise.all([
    solver.recaptcha('...', 'https://a.com'),
    solver.turnstile('...', 'https://b.com'),
  ]);
  console.log(r1.code, r2.code);
}

main();

AsyncCapSkip is exported as an alias of CapSkip — Node I/O is asynchronous by nature, so every method already returns a Promise.

More examples: examples/


Puppeteer, Playwright and Selenium

The SDK hands back a token; your existing browser tooling does the driving. The shape is the same whichever you use:

  1. Read the sitekey off the page (data-sitekey, or the widget's config object).
  2. Call the matching solve method with that sitekey and the page URL.
  3. Write the token into the response field and submit.

Puppeteer

const sitekey = await page.$eval('[data-sitekey]', el => el.dataset.sitekey);
const { code } = await solver.recaptcha(sitekey, page.url());

await page.evaluate(t => {
  document.getElementById('g-recaptcha-response').value = t;
}, code);
await page.click('button[type=submit]');

Playwright

const sitekey = await page.getAttribute('[data-sitekey]', 'data-sitekey');
const { code } = await solver.recaptcha(sitekey, page.url());

await page.evaluate(t => {
  document.getElementById('g-recaptcha-response').value = t;
}, code);

Selenium WebDriver

const el = await driver.findElement(By.css('[data-sitekey]'));
const sitekey = await el.getAttribute('data-sitekey');
const { code } = await solver.recaptcha(sitekey, await driver.getCurrentUrl());

await driver.executeScript(
  'document.getElementById("g-recaptcha-response").value = arguments[0];', code,
);

Longer walkthroughs: Puppeteer, Playwright and Selenium.


Return value

Every solve method resolves to:

{
  captchaId: '12345',    // internal ID from CapSkip
  code: 'TOKEN_OR_TEXT', // solution — text for image, token for reCAPTCHA/Turnstile
  userAgent: '...',      // Turnstile only — use when submitting challenge-page tokens
}

GeeTest additionally expands its answer into challenge, validate, and seccode, while code keeps the raw JSON string. CaptchaFox and Friendly Captcha add token, the same string as code. Capy expands its answer into captchakey, challengekey and answer — it returns an object rather than a token, and code keeps the raw answer. ALTCHA adds token (the same string as code) and number, the counter that solved it.


Error handling

const {
  CapSkip, ValidationException, NetworkException, ApiException, TimeoutException,
} = require('capskip');

try {
  const result = await solver.recaptcha('...', '...');
} catch (err) {
  if (err instanceof ValidationException) {
    // invalid parameters
  } else if (err instanceof NetworkException) {
    // CapSkip not running, or captcha not ready (manual polling)
  } else if (err instanceof ApiException) {
    // API returned an error code
  } else if (err instanceof TimeoutException) {
    // polling timeout exceeded
  }
}

TypeScript

Type definitions ship with the package — no @types install required.

import { CapSkip, SolveResult } from 'capskip';

const solver = new CapSkip({ host: '127.0.0.1', port: 8080 });
const result: SolveResult = await solver.recaptcha('...', 'https://example.com');

FAQ

How do I solve a captcha in Node.js?

Install the CapSkip desktop app, npm install capskip, then call the method that matches the widget — recaptcha(), turnstile(), geetest(), altcha(), capy(), captchafox(), friendlyCaptcha() or normal(). Each returns a Promise that resolves once CapSkip has an answer, giving you a token, or the recognized text in the case of an image captcha.

Is this a free captcha solver?

The SDK itself is MIT-licensed and free. Solving needs the CapSkip app, which is bought once rather than metered per captcha, so your cost stops scaling with volume.

Which captchas can it solve?

reCAPTCHA v2 (checkbox and invisible), reCAPTCHA v3, reCAPTCHA Enterprise, Cloudflare Turnstile, GeeTest v3, ALTCHA, Capy Puzzle, CaptchaFox, Friendly Captcha, and image/text captchas. Not hCaptcha, and not FunCaptcha/Arkose.

Does it work with Puppeteer and Playwright?

Yes — see above. The SDK never touches a browser itself, so it drops into whatever stack you already have, Selenium and plain fetch included.

Why is my reCAPTCHA v3 score low?

Google derives v3 scores from IP reputation, cookies and browsing history. A solver returns a valid token, but it cannot change how Google grades that token — score is forwarded as the target you want, not a guarantee. If a site enforces a high threshold, solve through a cleaner IP using the proxy option.

Can I use it as a 2captcha alternative?

Yes. CapSkip serves the same endpoints, so you can either move to this SDK or repoint an existing 2captcha client at 127.0.0.1:8080.

Does the captcha have to be on a public page?

For widget captchas, yes — CapSkip loads the URL you pass it. Image captchas only need the image, and that can be a local file.

Does it work with TypeScript and ESM?

Types ship in the package. The runtime is CommonJS, so import { CapSkip } from 'capskip' works from ESM through Node's interop.


Development

git clone https://github.com/capskip/capskip-node.git
cd capskip-node
npm install
npm test

The test suite uses Node's built-in test runner — no extra dependencies. See CONTRIBUTING.md for the full development workflow.


Links


License

MIT — see LICENSE.