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

n8n-nodes-captchaai-detect

v1.0.1

Published

CaptchaAI Detect community node for n8n - finds CAPTCHA widgets (reCAPTCHA, Turnstile, GeeTest, and more) on a page across every frame and returns their identifying parameters. Detection only - never solves, never injects tokens.

Readme

n8n-nodes-captchaai-detect

A detect-only n8n community node that finds CAPTCHA widgets on a page - reCAPTCHA (v2, v2 invisible, v2 enterprise, v3, v3 enterprise), Cloudflare Turnstile, GeeTest v3, FriendlyCaptcha, CaptchaFox, Lemin, reCAPTCHA image grids, BLS challenges, and Normal Image (OCR) captchas including BotDetect-style widgets - and returns their identifying parameters (sitekey, action, gt/challenge, an inline base64 image, and so on) as workflow items.

This node never solves a CAPTCHA. It never injects a token, never submits a form, and never calls any CaptchaAI (or other) solving API. It is a pure detector: point it at a URL, get back a structured description of what CAPTCHA widgets are on the page and what a solve call for each of them would need.

It is ported from the detection logic inside the CaptchaAI browser extension, running against a bundled headless Chromium (via Playwright) instead of as a browser extension - see "How it works" below for the frame-scanning and hook model this implies.


Install

Through n8n's UI: Settings → Community Nodes → Install, package name n8n-nodes-captchaai-detect.

Or with npm, inside your n8n installation:

npm install n8n-nodes-captchaai-detect

Chromium

This package bundles Playwright and downloads its own Chromium build automatically on install (postinstall runs playwright install chromium). Two things to know:

  • If your environment runs npm install --ignore-scripts (common in some Docker/CI setups), the automatic download is skipped. Run it manually once inside the package directory:

    npx playwright install chromium

    The node's own error message repeats this exact command if it ever fails to launch the browser.

  • Chromium itself needs OS-level shared libraries to run headless on Linux (fonts, libnss3, libatk, etc.). If you are running n8n in Docker, the official Playwright Docker images already have these; on a bare Debian/Ubuntu host you can install them with:

    npx playwright install-deps chromium

Not eligible for n8n's verified marketplace

This package bundles an external dependency (Playwright + a downloaded Chromium binary) and writes to the filesystem, which disqualifies it from n8n's verified community-node program (that program requires zero external dependencies and no filesystem access). This is a deliberate, accepted trade-off for a node whose entire job is driving a real browser - it is not something a future update is expected to "fix". Install it as a standard (unverified) community node.


How it works

  1. Launches the bundled Chromium and navigates to the given URL (networkidle, falling back to domcontentloaded + a fixed settle delay for pages that never truly go idle).
  2. Walks every frame on the page and runs every enabled DOM detector in each of them - this mirrors the source extension's own all_frames: true content script.
  3. A separate, top-frame-only init script hooks grecaptcha.render/execute, turnstile.render, and initGeetest, purely to observe their call parameters - it always calls through to the real function and never blocks or mocks page behavior. This, too, mirrors the source extension: its own hook script has no all_frames flag and therefore only ever runs in the top frame. A same-origin iframe that renders its own Turnstile widget or calls its own grecaptcha.execute() is still found by the DOM walk in step 2, just without the extra parameters (action, cData, etc.) that only exist in the JS call arguments.
  4. If you set Trigger Selector, it is clicked once after the page settles, then the hook store is re-read on a short bounded poll (with one final full DOM re-walk), since some widgets (reCAPTCHA v3, GeeTest v3, CaptchaFox) only produce their parameters after an interaction.
  5. Every detection is deduplicated by (type, sitekey, frameUrl, action) - the same sitekey found in two different frames is reported as two separate items, since a solve call for a widget nested in an iframe needs to know which frame it lives in.

Two intentional, documented exceptions to "detection only"

Everything above is passive - in particular, the reCAPTCHA v2 checkbox is never clicked to force a grid challenge open; a grid is only reported if it is already visible. Two things do interact with the page, though, both because there is no other way to see the data at all:

  • Closed shadow roots are widened to open. The top-frame hook init script patches Element.prototype.attachShadow so a mode: 'closed' request becomes mode: 'open' (still calling through to the real implementation) - Turnstile can render into a closed shadow root, and there is no way to inspect it otherwise.
  • CaptchaFox is clicked once. CaptchaFox's sitekey is never present in the DOM - it only appears as a path segment on a network request that fires when the widget is interacted with. If a CaptchaFox container is found and enabled, this node clicks it once (top frame only) and waits briefly for that request.

Node parameters

| Parameter | Type | Default | Notes | |---|---|---|---| | URL | string | — | Required. The page to load and scan. | | CAPTCHA Types | multi-select | all types | Restrict which CAPTCHA types to look for. | | Timeout (ms) | number | 30000 | Overall page-load budget. | | Trigger Selector | string | — | Optional CSS selector to click once after load, then poll briefly for interaction-only types (reCAPTCHA v3, GeeTest v3, CaptchaFox). | | Image Selector | string | — | Optional CSS selector for a specific Normal Image CAPTCHA image, when heuristic auto-detection isn't reliable enough. | | Input Selector | string | — | Optional CSS selector for the Normal Image answer field, used to locate the matching image when Image Selector isn't set. |

Output

One item per detected (and deduplicated) widget:

{
  "type": "recaptcha_v2",
  "pageurl": "https://example.com/signup",   // always the top-level page URL
  "frameUrl": "https://example.com/signup",  // the frame the widget was found in
  "source": "dom",                           // dom | hook | script-tag | network
  "sitekey": "6Lc...",
  "invisible": false,
  "enterprise": false,
  "solveParams": {                           // the exact params a solve API call would need
    "method": "userrecaptcha",
    "googlekey": "6Lc...",
    "pageurl": "https://example.com/signup"
  }
}

pageurl is always the top-level navigation URL; frameUrl is the specific frame the widget was found in (these differ for widgets nested inside an iframe). Image-based types (Normal Image, Grid Image) additionally carry imageBase64 at the top level for convenience. BLS returns one item with imagesBase64 (nine raw base64 strings, empty string for missing cells) and matching blsImage1…blsImage9 fields for the CaptchaAI solve node. Wire the solve node with CAPTCHA Type BLS and expressions such as {{ $json.blsImage1 }} … {{ $json.blsImage9 }}, {{ $json.instructions }}. solveParams still uses full JPEG data URIs as image_base64_1…image_base64_9 for direct API use.

A zero-detection run is not an error - it returns an empty item array. The node only throws on navigation failure, a browser-launch failure, or hitting the overall timeout (subject to n8n's "Continue On Fail").

GeeTest v4 is out of scope

The source extension has GeeTest v4 support disabled (GEETEST_V4_ENABLED = false); this node carries that forward exactly - there is no GeeTest v4 hook, detector, or output field.


Example workflow

See examples/detect-workflow.json - a Manual Trigger feeding a CaptchaAI Detect node, with its output passed to a Set node for inspection. Import it via Workflows → Import from File in n8n.


License & attribution

Licensed under the Apache License, Version 2.0, plus a short set of additional terms (attribution in your own README/NOTICE, a 30-day cure period on attribution defects, a trademark/logo carve-out, and a modification-disclosure requirement for forks). See LICENSE.md for the full text and NOTICE for the attribution notice to carry forward if you redistribute this project.