From eeef8c62887e1032459c5278469796ae14f93dcb Mon Sep 17 00:00:00 2001 From: dariyaanis Date: Wed, 28 Oct 2026 16:02:56 +0300 Subject: [PATCH 01/16] fix: validate required task parameters --- src/tasks.ts | 47 ++++++++++++++++++++++++++++++ tests/unit/task-validation.test.js | 43 +++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 tests/unit/task-validation.test.js diff --git a/src/tasks.ts b/src/tasks.ts index 7851c95..aa50b5b 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -1,5 +1,51 @@ import { ValidationError } from './exceptions.js'; +const REQUIRED_TASK_PARAMS: Record = { + RecaptchaV2TaskProxyless: ['websiteURL', 'websiteKey'], + RecaptchaV2Task: ['websiteURL', 'websiteKey', 'proxyType', 'proxyAddress', 'proxyPort'], + RecaptchaV2EnterpriseTaskProxyless: ['websiteURL', 'websiteKey'], + RecaptchaV2EnterpriseTask: ['websiteURL', 'websiteKey', 'proxyType', 'proxyAddress', 'proxyPort'], + RecaptchaV3TaskProxyless: ['websiteURL', 'websiteKey', 'minScore'], + TurnstileTaskProxyless: ['websiteURL', 'websiteKey'], + TurnstileTask: ['websiteURL', 'websiteKey', 'proxyType', 'proxyAddress', 'proxyPort'], + GeeTestTaskProxyless: ['websiteURL'], + GeeTestTask: ['websiteURL', 'proxyType', 'proxyAddress', 'proxyPort'], + ImageToTextTask: ['body'], + YandexSmartCaptchaTaskProxyless: ['websiteURL', 'websiteKey'], + YandexSmartCaptchaTask: ['websiteURL', 'websiteKey', 'proxyType', 'proxyAddress', 'proxyPort'], + CoordinatesTask: ['body'], + TencentTaskProxyless: ['websiteURL', 'appId'], + TencentTask: ['websiteURL', 'appId', 'proxyType', 'proxyAddress', 'proxyPort'] +}; + +function isMissing(value: unknown): boolean { + return value === null || value === undefined || (typeof value === 'string' && value.trim() === ''); +} + +function validateTaskParams(params: Record): void { + const type = params.type; + if (typeof type !== 'string') return; + + const required = [...(REQUIRED_TASK_PARAMS[type] || [])]; + if (type === 'GeeTestTask' || type === 'GeeTestTaskProxyless') { + if (params.version === 4) { + required.push('initParameters'); + } else { + required.push('gt', 'challenge'); + } + } + + const missing = required.filter((name) => isMissing(params[name])); + if (!missing.length && params.version === 4) { + const initParameters = params.initParameters as Record; + if (isMissing(initParameters.captcha_id)) missing.push('initParameters.captcha_id'); + } + + if (missing.length) { + throw new ValidationError(`${type} requires: ${missing.join(', ')}`); + } +} + /* ------------------------------------------------------------------ */ /* Shared parameter fragments */ /* ------------------------------------------------------------------ */ @@ -134,6 +180,7 @@ export class BaseTask = Record) { + validateTaskParams(params); this._params = params; } diff --git a/tests/unit/task-validation.test.js b/tests/unit/task-validation.test.js new file mode 100644 index 0000000..dbb3ff3 --- /dev/null +++ b/tests/unit/task-validation.test.js @@ -0,0 +1,43 @@ +import { Tasks, ValidationError } from '@captcha-solver-api/javascript-sdk'; + +describe('built-in task validation', () => { + test.each([ + ['RecaptchaV2Proxyless', { websiteURL: 'https://example.com' }, 'websiteKey'], + ['RecaptchaV2', { websiteURL: 'https://example.com', websiteKey: 'key' }, 'proxyType, proxyAddress, proxyPort'], + ['RecaptchaV2EnterpriseProxyless', { websiteKey: 'key' }, 'websiteURL'], + ['RecaptchaV3Proxyless', { websiteURL: 'https://example.com', websiteKey: 'key' }, 'minScore'], + ['TurnstileProxyless', { websiteURL: '', websiteKey: 'key' }, 'websiteURL'], + ['ImageToText', {}, 'body'], + ['YandexSmartCaptchaTaskProxyless', { websiteURL: 'https://example.com' }, 'websiteKey'], + ['CoordinatesTask', { body: ' ' }, 'body'], + ['TencentTaskProxyless', { websiteURL: 'https://example.com' }, 'appId'] + ])('%s reports missing required parameters', (className, params, expected) => { + expect(() => new Tasks[className](params)).toThrow(ValidationError); + expect(() => new Tasks[className](params)).toThrow(expected); + }); + + test('GeeTest v3 requires gt and challenge', () => { + expect(() => new Tasks.GeeTestProxyless({ + websiteURL: 'https://example.com', + gt: 'gt' + })).toThrow('GeeTestTaskProxyless requires: challenge'); + }); + + test('GeeTest v4 requires initParameters.captcha_id', () => { + expect(() => new Tasks.GeeTestProxyless({ + websiteURL: 'https://example.com', + version: 4, + initParameters: {} + })).toThrow('GeeTestTaskProxyless requires: initParameters.captcha_id'); + }); + + test('valid required parameters are accepted', () => { + expect(new Tasks.Turnstile({ + websiteURL: 'https://example.com', + websiteKey: 'key', + proxyType: 'http', + proxyAddress: '127.0.0.1', + proxyPort: 8080 + }).toDict()).toMatchObject({ type: 'TurnstileTask', proxyPort: 8080 }); + }); +}); From 78c3e8115cbb0da513ed022fc6ccd6ed2dd46e73 Mon Sep 17 00:00:00 2001 From: dariyaanis Date: Wed, 28 Oct 2026 16:03:16 +0300 Subject: [PATCH 02/16] docs: clarify captcha types without proxy variants --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 55c7326..382783b 100644 --- a/README.md +++ b/README.md @@ -119,15 +119,18 @@ on the target page. |---|---|---| | reCAPTCHA v2 | ✅ | ✅ | | reCAPTCHA v2 Enterprise | ✅ | ✅ | -| reCAPTCHA v3 | ✅ | ❌ | +| reCAPTCHA v3 | ✅ | — | | Cloudflare Turnstile | ✅ | ✅ | | GeeTest v3 | ✅ | ✅ | | GeeTest v4 | ✅ | ✅ | -| Image to Text | ✅ | ❌ | +| Image to Text | ✅ | — | | Yandex SmartCaptcha | ✅ | ✅ | -| Coordinates (click captcha) | ✅ | ❌ | +| Coordinates (click captcha) | ✅ | — | | Tencent | ✅ | ✅ | +`—` means that this CAPTCHA type does not have a separate proxy task method; +the supported task is used as shown in the Proxyless column. + ### Other supported API task types Use `GenericTask` for any task type supported by the API that does not have a dedicated convenience class: From 64795d2ba5a6893c0f1e32544fe766cffc69921a Mon Sep 17 00:00:00 2001 From: dariyaanis Date: Wed, 28 Oct 2026 16:03:42 +0300 Subject: [PATCH 03/16] docs: remove long polling interval from geetest examples --- README.md | 2 +- examples/README.md | 2 +- examples/async/README.md | 2 +- examples/async/geetest_v3.js | 2 +- examples/async/geetest_v4.js | 2 +- examples/sync/README.md | 2 +- examples/sync/geetest_v3.js | 2 +- examples/sync/geetest_v4.js | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 382783b..5b0940d 100644 --- a/README.md +++ b/README.md @@ -422,7 +422,7 @@ Docs: [v3 ↗](https://captcha-solver.com/en/docs/captcha-types#geetest-v3), [v4 ```javascript import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; -const captchaSolver = new CaptchaClient({ clientKey: 'your_api_key', timeout: 300000, pollingInterval: 10000 }); +const captchaSolver = new CaptchaClient({ clientKey: 'your_api_key', timeout: 300000 }); const task = new Tasks.GeeTestProxyless({ websiteURL: 'https://example.com/login', gt: 'f2ae6cadcf7886856696c46d84d109d1', diff --git a/examples/README.md b/examples/README.md index 044fbd6..ed9dbb1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -114,7 +114,7 @@ the page and passed along. The only example that performs a **request of its own before solving**. The `challenge` value is session-specific and must be fresh for every task, so the script fetches one first and then builds the task. The fetch URL is a deliberate placeholder — see [Before you run](#before-you-run). Uses -`timeout: 300000` and `pollingInterval: 10000`, since GeeTest is among the slowest types. `version` +`timeout: 300000`, since GeeTest is among the slowest types. `version` is omitted because v3 is the default. ### GeeTest v4 diff --git a/examples/async/README.md b/examples/async/README.md index 9015c41..afb2eeb 100644 --- a/examples/async/README.md +++ b/examples/async/README.md @@ -95,7 +95,7 @@ That URL — `https://target-site.com/path/to/geetest/init` — is a **placehold not run end-to-end unmodified. It marks where the fetch belongs in the flow; point it at your real source of `challenge` first. -The client uses `timeout: 300000` and `pollingInterval: 10000`, GeeTest being among the slowest +The client uses `timeout: 300000`, GeeTest being among the slowest types. `version` is omitted since v3 is the default. Solution: `challenge`, `validate`, `seccode`. ### geetest_v4.js diff --git a/examples/async/geetest_v3.js b/examples/async/geetest_v3.js index 1363776..f8a51d9 100644 --- a/examples/async/geetest_v3.js +++ b/examples/async/geetest_v3.js @@ -18,7 +18,7 @@ import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; // GeeTest tasks may take longer. Increase timeout if needed. -const captchaSolver = new CaptchaClient({ clientKey: apiKey, timeout: 300000, pollingInterval: 10000 }); +const captchaSolver = new CaptchaClient({ clientKey: apiKey, timeout: 300000 }); // Fetch a fresh challenge value from the target page. // In production, extract this from the page's initGeetest call or network requests. diff --git a/examples/async/geetest_v4.js b/examples/async/geetest_v4.js index 26d7a76..ffcf4b9 100644 --- a/examples/async/geetest_v4.js +++ b/examples/async/geetest_v4.js @@ -13,7 +13,7 @@ import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; // GeeTest v4 tasks may take longer. Increase timeout if needed. -const captchaSolver = new CaptchaClient({ clientKey: apiKey, timeout: 300000, pollingInterval: 10000 }); +const captchaSolver = new CaptchaClient({ clientKey: apiKey, timeout: 300000 }); // --- Proxyless example --- // v4 drops gt/challenge. The widget is identified by captcha_id inside initParameters. diff --git a/examples/sync/README.md b/examples/sync/README.md index 88fd009..67c29cb 100644 --- a/examples/sync/README.md +++ b/examples/sync/README.md @@ -104,7 +104,7 @@ That URL — `https://target-site.com/path/to/geetest/init` — is a **placehold not run end-to-end unmodified. It marks where the fetch belongs in the flow; point it at your real source of `challenge` first. -The client uses `timeout: 300000` and `pollingInterval: 10000`, GeeTest being among the slowest +The client uses `timeout: 300000`, GeeTest being among the slowest types. `version` is omitted since v3 is the default. Solution: `challenge`, `validate`, `seccode`. ### geetest_v4.js diff --git a/examples/sync/geetest_v3.js b/examples/sync/geetest_v3.js index 0840845..22cb60b 100644 --- a/examples/sync/geetest_v3.js +++ b/examples/sync/geetest_v3.js @@ -18,7 +18,7 @@ import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; // GeeTest tasks may take longer. Increase timeout if needed. -const captchaSolver = new CaptchaClient({ clientKey: apiKey, timeout: 300000, pollingInterval: 10000 }); +const captchaSolver = new CaptchaClient({ clientKey: apiKey, timeout: 300000 }); // Fetch a fresh challenge value from the target page. // In production, extract this from the page's initGeetest call or network requests. diff --git a/examples/sync/geetest_v4.js b/examples/sync/geetest_v4.js index 822147b..4915967 100644 --- a/examples/sync/geetest_v4.js +++ b/examples/sync/geetest_v4.js @@ -13,7 +13,7 @@ import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; // GeeTest v4 tasks may take longer. Increase timeout if needed. -const captchaSolver = new CaptchaClient({ clientKey: apiKey, timeout: 300000, pollingInterval: 10000 }); +const captchaSolver = new CaptchaClient({ clientKey: apiKey, timeout: 300000 }); // --- Proxyless example --- // v4 drops gt/challenge. The widget is identified by captcha_id inside initParameters. From 050ae80dfe9d22a07cbe789fa8fa388e39e26fc5 Mon Sep 17 00:00:00 2001 From: dariyaanis Date: Wed, 28 Oct 2026 16:04:12 +0300 Subject: [PATCH 04/16] ci: test against Node.js 24 --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b30cfc4..f8500da 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,7 +17,7 @@ jobs: fail-fast: false matrix: # Matches engines.node: ">=18" in package.json - node-version: [18, 20, 22] + node-version: [18, 20, 22, 24] steps: - uses: actions/checkout@v4 From af6a760b4ff0e4073472f89bfd82c417fd59c08a Mon Sep 17 00:00:00 2001 From: dariyaanis Date: Wed, 28 Oct 2026 16:04:28 +0300 Subject: [PATCH 05/16] docs: add security policy --- SECURITY.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..dbee401 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,28 @@ +# Security Policy + +## Supported versions + +Security fixes are provided for the latest `1.x` release of the SDK. + +| Version | Supported | +|---|---| +| 1.x | Yes | +| < 1.0 | No | + +## Reporting a vulnerability + +Please do not report security vulnerabilities in public GitHub issues. + +Use GitHub's private vulnerability reporting instead: + +1. Open the repository's **Security** tab. +2. Select **Advisories**. +3. Select **Report a vulnerability**. + +Include the affected SDK version, a clear description of the issue, steps to +reproduce it, and its potential impact. If possible, include a minimal proof of +concept and any suggested mitigation. + +We will acknowledge the report, investigate it, and coordinate disclosure with +the reporter. Please allow time for a fix to be prepared and released before +publishing details. From 1f71162ce3f239dc6397ce82e0480d4f0aa1728e Mon Sep 17 00:00:00 2001 From: dariyaanis Date: Wed, 28 Oct 2026 16:04:47 +0300 Subject: [PATCH 06/16] docs: link example names in requirements table --- examples/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/README.md b/examples/README.md index ed9dbb1..243ab4a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -164,14 +164,14 @@ Every example except `balance.js` needs something replaced first. | Example | What you must supply | |---|---| -| `balance.js` | Nothing — runs as-is with a valid key | -| `turnstile.js` | Real `websiteURL` and `websiteKey`; for Challenge pages also `action`, `data`, `pagedata` | -| `yandex_smartcaptcha.js` | Real `websiteURL` and `websiteKey` | -| `tencent.js` | Real `websiteURL` and `appId` | -| `geetest_v3.js` | Real `websiteURL` and `gt`, **plus a real init endpoint** (see below) | -| `geetest_v4.js` | Real `websiteURL` and `captcha_id` | -| `image_to_text.js` | Nothing — all sample images ship in [assets/](assets/) | -| `coordinates.js` | Nothing — all sample images ship in [assets/](assets/) | +| [`balance.js`](async/balance.js) | Nothing — runs as-is with a valid key | +| [`turnstile.js`](async/turnstile.js) | Real `websiteURL` and `websiteKey`; for Challenge pages also `action`, `data`, `pagedata` | +| [`yandex_smartcaptcha.js`](async/yandex_smartcaptcha.js) | Real `websiteURL` and `websiteKey` | +| [`tencent.js`](async/tencent.js) | Real `websiteURL` and `appId` | +| [`geetest_v3.js`](async/geetest_v3.js) | Real `websiteURL` and `gt`, **plus a real init endpoint** (see below) | +| [`geetest_v4.js`](async/geetest_v4.js) | Real `websiteURL` and `captcha_id` | +| [`image_to_text.js`](async/image_to_text.js) | Nothing — all sample images ship in [assets/](assets/) | +| [`coordinates.js`](async/coordinates.js) | Nothing — all sample images ship in [assets/](assets/) | **Both image examples run entirely as-is**, with a valid key and nothing else — every block, [assets/](assets/) and are read through `new URL('../assets/…', import.meta.url)`, which resolves From 0ba604c33cf90f8b555d070482c7c55a996ab754 Mon Sep 17 00:00:00 2001 From: dariyaanis Date: Wed, 28 Oct 2026 16:05:30 +0300 Subject: [PATCH 07/16] docs: clarify solution response fields --- README.md | 55 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 5b0940d..dd50ec5 100644 --- a/README.md +++ b/README.md @@ -231,7 +231,9 @@ Choose the proxy variant when the solving session must use your own IP address. | `userAgent` | no | User-Agent to solve with. Recommended to match the agent submitting the token. | | `cookies` | no | Session cookies to use while solving, if the page requires them. | -**Response:** `gRecaptchaResponse` -- submit as `g-recaptcha-response`. +**Response fields** + +- `gRecaptchaResponse` — submit this token as `g-recaptcha-response`. ```javascript import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; @@ -273,7 +275,9 @@ as reCAPTCHA v2, plus: | `enterprisePayload` | no | Extra parameters passed to `grecaptcha.enterprise.render` on the page, e.g. `{ s: '...' }`. | | `apiDomain` | no | Defaults to `google.com`. | -**Response:** `gRecaptchaResponse`. +**Response fields** + +- `gRecaptchaResponse` — submit this token to the Enterprise reCAPTCHA callback. ```javascript import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; @@ -305,7 +309,9 @@ this task only without a customer proxy. | `isEnterprise` | no | Set to `true` for reCAPTCHA v3 Enterprise. | | `apiDomain` | no | Alternative domain used to load the reCAPTCHA script. | -**Response:** `gRecaptchaResponse`. +**Response fields** + +- `gRecaptchaResponse` — submit this token to the reCAPTCHA v3 callback. ```javascript import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; @@ -340,10 +346,15 @@ that the target page expects in `cf-turnstile-response`. | `pagedata` | no | Value of the `chlPageData` parameter, needed for some Cloudflare challenge pages beyond the basic widget. Note the lowercase, single-word spelling -- unlike every other field on this page, the real API does not accept `pageData`. | | `userAgent` | no | Current browser User-Agent. Pass it for Cloudflare Challenge pages; it is not needed for a standalone widget. | -**Response:** `token` -- submit as `cf-turnstile-response`. For Cloudflare Challenge -pages, pass `action`, `data`, `pagedata`, and the current browser `userAgent` in the -task. Keep the browser User-Agent unchanged when invoking the callback. The solution -may also contain `userAgent`; do not switch the browser to this returned value. +**Response fields** + +- `token` — submit this value as `cf-turnstile-response`. +- `userAgent` — the User-Agent reported by the solving service, when present. + +> **Important:** For Cloudflare Challenge pages, pass `action`, `data`, +> `pagedata`, and the current browser `userAgent` in the task. Keep that browser +> User-Agent unchanged when invoking the callback; do not switch it to the +> returned value. ```javascript import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; @@ -379,7 +390,9 @@ browser session involved. | `comment` | no | Free-text hint for the worker. | | `imgInstructions` | no | Optional supplementary instruction image, base64-encoded. | -**Response:** `text` -- the recognized text/answer. +**Response fields** + +- `text` — the recognized text or calculated answer. ```javascript import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; @@ -417,7 +430,14 @@ pass the values collected from the target page before creating the task. | `userAgent` | no | User-Agent to solve with. | | `risk_type` | no | Dynamic, single-use value included in the page's captcha-loading request, if present. Note the snake_case name -- unlike every other field here, the real API does not accept `riskType`. | -**Response:** v3 -- `challenge`, `validate`, `seccode`. v4 -- `captcha_id`, `lot_number`, `pass_token`, `gen_time`, `captcha_output`. +**Response fields** + +- GeeTest v3: `challenge`, `validate`, and `seccode`. +- GeeTest v4: `captcha_id`, `lot_number`, `pass_token`, `gen_time`, and + `captcha_output`. + +> **Important:** Fetch a fresh v3 `challenge` for every task. Do not reuse a +> challenge value from an earlier request. Docs: [v3 ↗](https://captcha-solver.com/en/docs/captcha-types#geetest-v3), [v4 ↗](https://captcha-solver.com/en/docs/captcha-types#geetest-v4) ```javascript @@ -468,7 +488,9 @@ challenge. For the image challenge instead, use `Tasks.CoordinatesTask` with Proxy variant note: `proxyType` also accepts `'https'` for this captcha type only (in addition to `http`/`socks4`/`socks5`). -**Response:** `token`. +**Response fields** + +- `token` — submit this value to the SmartCaptcha callback. ```javascript import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; @@ -501,7 +523,9 @@ directly. | `minClicks` | no | Minimum number of clicks expected (default `1`). | | `maxClicks` | no | Maximum number of clicks allowed. | -**Response:** `coordinates` -- an array of `{ x, y }` pixel positions to click, in order. +**Response fields** + +- `coordinates` — an ordered array of `{ x, y }` pixel positions to click. ```javascript import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; @@ -533,7 +557,14 @@ values required by the target page. | `appId` | yes | Value of the `appId` parameter found in the page source. | | `captchaScript` | no | URL of the Tencent captcha script, if the page uses a non-default one. | -**Response:** `appid`, `ret`, `ticket`, `randstr` -- pass all four into the page's Tencent captcha callback. +**Response fields** + +- `appid` — Tencent application ID. +- `ret` — Tencent result code. +- `ticket` — verification ticket. +- `randstr` — verification nonce. + +Pass all four values to the page's Tencent CAPTCHA callback. ```javascript import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; From 109ea516f541fa58a497ceb01cc5510474e7fe74 Mon Sep 17 00:00:00 2001 From: dariyaanis Date: Wed, 28 Oct 2026 16:16:56 +0300 Subject: [PATCH 08/16] ci: run integration tests on pull requests --- .github/workflows/tests.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f8500da..360da34 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -51,11 +51,10 @@ jobs: integration: name: Integration (real API) - # Only on schedule and manual dispatch. Deliberately not run on - # pull_request: the tests spend real account balance, and secrets are - # unavailable in PRs from forks anyway -- the tests would silently skip - # and produce a false-green result. - if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + # Run for same-repository pull requests as well as scheduled and manual + # checks. GitHub does not expose repository secrets to pull requests from + # forks, so those runs fail at the explicit secret check below instead of + # producing a false-green result. runs-on: ubuntu-latest steps: From 3c021e8eb15ea63575ad8421ece84d41096d222c Mon Sep 17 00:00:00 2001 From: dariyaanis Date: Thu, 29 Oct 2026 12:34:40 +0300 Subject: [PATCH 09/16] fix: validate omitted constructor options --- src/client.ts | 2 +- src/tasks.ts | 32 +++++++++++++++--------------- tests/unit/async/client.test.js | 1 + tests/unit/sync/client.test.js | 1 + tests/unit/task-validation.test.js | 7 +++++++ 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/client.ts b/src/client.ts index fda67c0..f477809 100644 --- a/src/client.ts +++ b/src/client.ts @@ -75,7 +75,7 @@ export class CaptchaClient { * @param options - Client options; only `clientKey` is required. * @throws {ValidationError} If `clientKey` is empty. */ - constructor({ clientKey, baseUrl = 'https://api.captcha-solver.com', timeout = 120000, pollingInterval = 5000 }: CaptchaClientOptions) { + constructor({ clientKey, baseUrl = 'https://api.captcha-solver.com', timeout = 120000, pollingInterval = 5000 }: CaptchaClientOptions = {} as CaptchaClientOptions) { if (!clientKey) throw new ValidationError('clientKey is required'); this.clientKey = clientKey; this.baseUrl = baseUrl; this.timeout = timeout; this.pollingInterval = pollingInterval; } diff --git a/src/tasks.ts b/src/tasks.ts index aa50b5b..99b632f 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -227,7 +227,7 @@ export class GenericTask extends BaseTask { * @param params - Task payload; `type` is required. * @throws {ValidationError} If `type` is missing. */ - constructor({ type, ...params }: GenericTaskParams) { + constructor({ type, ...params }: GenericTaskParams = {} as GenericTaskParams) { if (!type) throw new ValidationError('task type is required'); super({ type, ...params }); } @@ -265,7 +265,7 @@ export interface RecaptchaV2Params extends RecaptchaV2ProxylessParams, ProxyPara * @see https://captcha-solver.com/en/docs/captcha-types#recaptcha-v2 */ export class RecaptchaV2Proxyless extends BaseTask { - constructor({ websiteURL, websiteKey, isInvisible = null, recaptchaDataSValue = null, apiDomain = null, cookies = null, userAgent = null }: RecaptchaV2ProxylessParams) { + constructor({ websiteURL, websiteKey, isInvisible = null, recaptchaDataSValue = null, apiDomain = null, cookies = null, userAgent = null }: RecaptchaV2ProxylessParams = {} as RecaptchaV2ProxylessParams) { super({ type: 'RecaptchaV2TaskProxyless', websiteURL, websiteKey, isInvisible, recaptchaDataSValue, apiDomain, cookies, userAgent }); } } @@ -277,7 +277,7 @@ export class RecaptchaV2Proxyless extends BaseTask { * @see https://captcha-solver.com/en/docs/captcha-types#recaptcha-v2 */ export class RecaptchaV2 extends BaseTask { - constructor({ websiteURL, websiteKey, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null, isInvisible = null, recaptchaDataSValue = null, apiDomain = null, cookies = null, userAgent = null }: RecaptchaV2Params) { + constructor({ websiteURL, websiteKey, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null, isInvisible = null, recaptchaDataSValue = null, apiDomain = null, cookies = null, userAgent = null }: RecaptchaV2Params = {} as RecaptchaV2Params) { super({ type: 'RecaptchaV2Task', websiteURL, websiteKey, proxyType, proxyAddress, proxyPort, proxyLogin, proxyPassword, isInvisible, recaptchaDataSValue, apiDomain, cookies, userAgent }); } } @@ -317,7 +317,7 @@ export interface RecaptchaV2EnterpriseParams extends RecaptchaV2EnterpriseProxyl * @see https://captcha-solver.com/en/docs/captcha-types#recaptcha-v2-enterprise */ export class RecaptchaV2EnterpriseProxyless extends BaseTask { - constructor({ websiteURL, websiteKey, enterprisePayload = null, isInvisible = null, apiDomain = null, cookies = null, userAgent = null }: RecaptchaV2EnterpriseProxylessParams) { + constructor({ websiteURL, websiteKey, enterprisePayload = null, isInvisible = null, apiDomain = null, cookies = null, userAgent = null }: RecaptchaV2EnterpriseProxylessParams = {} as RecaptchaV2EnterpriseProxylessParams) { super({ type: 'RecaptchaV2EnterpriseTaskProxyless', websiteURL, websiteKey, enterprisePayload, isInvisible, apiDomain, cookies, userAgent }); } } @@ -329,7 +329,7 @@ export class RecaptchaV2EnterpriseProxyless extends BaseTask * @see https://captcha-solver.com/en/docs/captcha-types#recaptcha-v2-enterprise */ export class RecaptchaV2Enterprise extends BaseTask { - constructor({ websiteURL, websiteKey, enterprisePayload = null, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null, isInvisible = null, apiDomain = null, cookies = null, userAgent = null }: RecaptchaV2EnterpriseParams) { + constructor({ websiteURL, websiteKey, enterprisePayload = null, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null, isInvisible = null, apiDomain = null, cookies = null, userAgent = null }: RecaptchaV2EnterpriseParams = {} as RecaptchaV2EnterpriseParams) { super({ type: 'RecaptchaV2EnterpriseTask', websiteURL, websiteKey, enterprisePayload, proxyType, proxyAddress, proxyPort, proxyLogin, proxyPassword, isInvisible, apiDomain, cookies, userAgent }); } } @@ -362,7 +362,7 @@ export interface RecaptchaV3ProxylessParams { * @see https://captcha-solver.com/en/docs/captcha-types#recaptcha-v3 */ export class RecaptchaV3Proxyless extends BaseTask { - constructor({ websiteURL, websiteKey, minScore, pageAction = null, isEnterprise = null, apiDomain = null }: RecaptchaV3ProxylessParams) { + constructor({ websiteURL, websiteKey, minScore, pageAction = null, isEnterprise = null, apiDomain = null }: RecaptchaV3ProxylessParams = {} as RecaptchaV3ProxylessParams) { super({ type: 'RecaptchaV3TaskProxyless', websiteURL, websiteKey, minScore, pageAction, isEnterprise, apiDomain }); } } @@ -404,7 +404,7 @@ export interface TurnstileParams extends TurnstileProxylessParams, ProxyParams { * @see https://captcha-solver.com/en/docs/captcha-types#cloudflare-turnstile */ export class TurnstileProxyless extends BaseTask { - constructor({ websiteURL, websiteKey, action = null, data = null, pagedata = null, userAgent = null }: TurnstileProxylessParams) { + constructor({ websiteURL, websiteKey, action = null, data = null, pagedata = null, userAgent = null }: TurnstileProxylessParams = {} as TurnstileProxylessParams) { super({ type: 'TurnstileTaskProxyless', websiteURL, websiteKey, action, data, pagedata, userAgent }); } } @@ -416,7 +416,7 @@ export class TurnstileProxyless extends BaseTask { * @see https://captcha-solver.com/en/docs/captcha-types#cloudflare-turnstile */ export class Turnstile extends BaseTask { - constructor({ websiteURL, websiteKey, action = null, data = null, pagedata = null, userAgent = null, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null }: TurnstileParams) { + constructor({ websiteURL, websiteKey, action = null, data = null, pagedata = null, userAgent = null, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null }: TurnstileParams = {} as TurnstileParams) { super({ type: 'TurnstileTask', websiteURL, websiteKey, action, data, pagedata, userAgent, proxyType, proxyAddress, proxyPort, proxyLogin, proxyPassword }); } } @@ -466,7 +466,7 @@ export interface GeeTestParams extends GeeTestProxylessParams, ProxyParams {} * @see https://captcha-solver.com/en/docs/captcha-types#geetest-v4 */ export class GeeTestProxyless extends BaseTask { - constructor({ websiteURL, gt = null, challenge = null, version = null, initParameters = null, geetestApiServerSubdomain = null, userAgent = null, risk_type = null }: GeeTestProxylessParams) { + constructor({ websiteURL, gt = null, challenge = null, version = null, initParameters = null, geetestApiServerSubdomain = null, userAgent = null, risk_type = null }: GeeTestProxylessParams = {} as GeeTestProxylessParams) { super({ type: 'GeeTestTaskProxyless', websiteURL, gt, challenge, version, initParameters, geetestApiServerSubdomain, userAgent, risk_type }); } } @@ -479,7 +479,7 @@ export class GeeTestProxyless extends BaseTask { * @see https://captcha-solver.com/en/docs/captcha-types#geetest-v4 */ export class GeeTest extends BaseTask { - constructor({ websiteURL, gt = null, challenge = null, version = null, initParameters = null, geetestApiServerSubdomain = null, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null, userAgent = null, risk_type = null }: GeeTestParams) { + constructor({ websiteURL, gt = null, challenge = null, version = null, initParameters = null, geetestApiServerSubdomain = null, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null, userAgent = null, risk_type = null }: GeeTestParams = {} as GeeTestParams) { super({ type: 'GeeTestTask', websiteURL, gt, challenge, version, initParameters, geetestApiServerSubdomain, proxyType, proxyAddress, proxyPort, proxyLogin, proxyPassword, userAgent, risk_type }); } } @@ -528,7 +528,7 @@ export interface ImageToTextParams { * @see https://captcha-solver.com/en/docs/captcha-types#image-to-text */ export class ImageToText extends BaseTask { - constructor({ body, phrase = null, case_ = null, numeric = null, math = null, minLength = null, maxLength = null, comment = null, imgInstructions = null }: ImageToTextParams) { + constructor({ body, phrase = null, case_ = null, numeric = null, math = null, minLength = null, maxLength = null, comment = null, imgInstructions = null }: ImageToTextParams = {} as ImageToTextParams) { super({ type: 'ImageToTextTask', body, phrase, case: case_, numeric, math, minLength, maxLength, comment, imgInstructions }); } } @@ -563,7 +563,7 @@ export interface YandexSmartCaptchaTaskParams extends YandexSmartCaptchaTaskProx * @see https://captcha-solver.com/en/docs/captcha-types#yandex-smartcaptcha */ export class YandexSmartCaptchaTaskProxyless extends BaseTask { - constructor({ websiteURL, websiteKey, userAgent = null, cookies = null }: YandexSmartCaptchaTaskProxylessParams) { + constructor({ websiteURL, websiteKey, userAgent = null, cookies = null }: YandexSmartCaptchaTaskProxylessParams = {} as YandexSmartCaptchaTaskProxylessParams) { super({ type: 'YandexSmartCaptchaTaskProxyless', websiteURL, websiteKey, userAgent, cookies }); } } @@ -575,7 +575,7 @@ export class YandexSmartCaptchaTaskProxyless extends BaseTask { - constructor({ websiteURL, websiteKey, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null, userAgent = null, cookies = null }: YandexSmartCaptchaTaskParams) { + constructor({ websiteURL, websiteKey, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null, userAgent = null, cookies = null }: YandexSmartCaptchaTaskParams = {} as YandexSmartCaptchaTaskParams) { super({ type: 'YandexSmartCaptchaTask', websiteURL, websiteKey, proxyType, proxyAddress, proxyPort, proxyLogin, proxyPassword, userAgent, cookies }); } } @@ -606,7 +606,7 @@ export interface CoordinatesTaskParams { * @see https://captcha-solver.com/en/docs/captcha-types#coordinates */ export class CoordinatesTask extends BaseTask { - constructor({ body, comment = null, imgInstructions = null, minClicks = null, maxClicks = null }: CoordinatesTaskParams) { + constructor({ body, comment = null, imgInstructions = null, minClicks = null, maxClicks = null }: CoordinatesTaskParams = {} as CoordinatesTaskParams) { super({ type: 'CoordinatesTask', body, comment, imgInstructions, minClicks, maxClicks }); } } @@ -635,7 +635,7 @@ export interface TencentTaskParams extends TencentTaskProxylessParams, ProxyPara * @see https://captcha-solver.com/en/docs/captcha-types#tencent */ export class TencentTaskProxyless extends BaseTask { - constructor({ websiteURL, appId, captchaScript = null }: TencentTaskProxylessParams) { + constructor({ websiteURL, appId, captchaScript = null }: TencentTaskProxylessParams = {} as TencentTaskProxylessParams) { super({ type: 'TencentTaskProxyless', websiteURL, appId, captchaScript }); } } @@ -647,7 +647,7 @@ export class TencentTaskProxyless extends BaseTask { * @see https://captcha-solver.com/en/docs/captcha-types#tencent */ export class TencentTask extends BaseTask { - constructor({ websiteURL, appId, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null, captchaScript = null }: TencentTaskParams) { + constructor({ websiteURL, appId, proxyType, proxyAddress, proxyPort, proxyLogin = null, proxyPassword = null, captchaScript = null }: TencentTaskParams = {} as TencentTaskParams) { super({ type: 'TencentTask', websiteURL, appId, proxyType, proxyAddress, proxyPort, proxyLogin, proxyPassword, captchaScript }); } } diff --git a/tests/unit/async/client.test.js b/tests/unit/async/client.test.js index 8558f62..b9f8cd5 100644 --- a/tests/unit/async/client.test.js +++ b/tests/unit/async/client.test.js @@ -25,6 +25,7 @@ describe('CaptchaClient (async)', () => { test('throws ValidationError when clientKey is missing', () => { expect(() => new CaptchaClient({})).toThrow(ValidationError); + expect(() => new CaptchaClient()).toThrow(ValidationError); }); test('sends the SDK version header', async () => { diff --git a/tests/unit/sync/client.test.js b/tests/unit/sync/client.test.js index 90acac9..f107285 100644 --- a/tests/unit/sync/client.test.js +++ b/tests/unit/sync/client.test.js @@ -26,6 +26,7 @@ describe('CaptchaClient (sync-style)', () => { test('throws ValidationError when clientKey is missing', () => { expect(() => new CaptchaClient({})).toThrow(ValidationError); + expect(() => new CaptchaClient()).toThrow(ValidationError); }); test('creates task and returns taskId', () => { diff --git a/tests/unit/task-validation.test.js b/tests/unit/task-validation.test.js index dbb3ff3..b152cdc 100644 --- a/tests/unit/task-validation.test.js +++ b/tests/unit/task-validation.test.js @@ -1,6 +1,13 @@ import { Tasks, ValidationError } from '@captcha-solver-api/javascript-sdk'; describe('built-in task validation', () => { + test('missing constructor arguments produce SDK validation errors', () => { + expect(() => new Tasks.ImageToText()).toThrow(ValidationError); + expect(() => new Tasks.RecaptchaV2Proxyless()).toThrow( + 'RecaptchaV2TaskProxyless requires: websiteURL, websiteKey' + ); + }); + test.each([ ['RecaptchaV2Proxyless', { websiteURL: 'https://example.com' }, 'websiteKey'], ['RecaptchaV2', { websiteURL: 'https://example.com', websiteKey: 'key' }, 'proxyType, proxyAddress, proxyPort'], From 2fbe1318681c3b4c42c134acfecba14436ed3b4d Mon Sep 17 00:00:00 2001 From: dariyaanis Date: Thu, 29 Oct 2026 12:36:37 +0300 Subject: [PATCH 10/16] docs: split and standardize promise examples --- README.md | 108 +++++++----- examples/README.md | 205 ++++------------------ examples/async/README.md | 153 ---------------- examples/async/balance.js | 23 --- examples/async/coordinates.js | 63 ------- examples/async/geetest_v3.js | 71 -------- examples/async/geetest_v4.js | 56 ------ examples/async/image_to_text.js | 86 --------- examples/async/recaptcha_v2.js | 52 ------ examples/async/recaptcha_v2_enterprise.js | 55 ------ examples/async/recaptcha_v3.js | 25 --- examples/async/tencent.js | 52 ------ examples/async/turnstile.js | 58 ------ examples/async/yandex_smartcaptcha.js | 51 ------ examples/{sync => }/balance.js | 0 examples/coordinates.js | 13 ++ examples/coordinates_traffic_lights.js | 15 ++ examples/geetest_v3.js | 14 ++ examples/geetest_v3_proxy.js | 17 ++ examples/geetest_v4.js | 13 ++ examples/geetest_v4_proxy.js | 16 ++ examples/image_to_text.js | 15 ++ examples/image_to_text_language_pool.js | 12 ++ examples/image_to_text_math.js | 15 ++ examples/recaptcha_v2.js | 12 ++ examples/recaptcha_v2_enterprise.js | 12 ++ examples/recaptcha_v2_enterprise_proxy.js | 15 ++ examples/recaptcha_v2_proxy.js | 17 ++ examples/{sync => }/recaptcha_v3.js | 0 examples/sync/README.md | 164 ----------------- examples/sync/coordinates.js | 67 ------- examples/sync/geetest_v3.js | 73 -------- examples/sync/geetest_v4.js | 60 ------- examples/sync/image_to_text.js | 92 ---------- examples/sync/recaptcha_v2.js | 56 ------ examples/sync/recaptcha_v2_enterprise.js | 59 ------- examples/sync/tencent.js | 56 ------ examples/sync/turnstile.js | 62 ------- examples/sync/yandex_smartcaptcha.js | 55 ------ examples/tencent.js | 12 ++ examples/tencent_proxy.js | 15 ++ examples/turnstile.js | 12 ++ examples/turnstile_proxy.js | 15 ++ examples/yandex_smartcaptcha.js | 12 ++ examples/yandex_smartcaptcha_proxy.js | 15 ++ 45 files changed, 361 insertions(+), 1708 deletions(-) delete mode 100644 examples/async/README.md delete mode 100644 examples/async/balance.js delete mode 100644 examples/async/coordinates.js delete mode 100644 examples/async/geetest_v3.js delete mode 100644 examples/async/geetest_v4.js delete mode 100644 examples/async/image_to_text.js delete mode 100644 examples/async/recaptcha_v2.js delete mode 100644 examples/async/recaptcha_v2_enterprise.js delete mode 100644 examples/async/recaptcha_v3.js delete mode 100644 examples/async/tencent.js delete mode 100644 examples/async/turnstile.js delete mode 100644 examples/async/yandex_smartcaptcha.js rename examples/{sync => }/balance.js (100%) create mode 100644 examples/coordinates.js create mode 100644 examples/coordinates_traffic_lights.js create mode 100644 examples/geetest_v3.js create mode 100644 examples/geetest_v3_proxy.js create mode 100644 examples/geetest_v4.js create mode 100644 examples/geetest_v4_proxy.js create mode 100644 examples/image_to_text.js create mode 100644 examples/image_to_text_language_pool.js create mode 100644 examples/image_to_text_math.js create mode 100644 examples/recaptcha_v2.js create mode 100644 examples/recaptcha_v2_enterprise.js create mode 100644 examples/recaptcha_v2_enterprise_proxy.js create mode 100644 examples/recaptcha_v2_proxy.js rename examples/{sync => }/recaptcha_v3.js (100%) delete mode 100644 examples/sync/README.md delete mode 100644 examples/sync/coordinates.js delete mode 100644 examples/sync/geetest_v3.js delete mode 100644 examples/sync/geetest_v4.js delete mode 100644 examples/sync/image_to_text.js delete mode 100644 examples/sync/recaptcha_v2.js delete mode 100644 examples/sync/recaptcha_v2_enterprise.js delete mode 100644 examples/sync/tencent.js delete mode 100644 examples/sync/turnstile.js delete mode 100644 examples/sync/yandex_smartcaptcha.js create mode 100644 examples/tencent.js create mode 100644 examples/tencent_proxy.js create mode 100644 examples/turnstile.js create mode 100644 examples/turnstile_proxy.js create mode 100644 examples/yandex_smartcaptcha.js create mode 100644 examples/yandex_smartcaptcha_proxy.js diff --git a/README.md b/README.md index dd50ec5..206a43b 100644 --- a/README.md +++ b/README.md @@ -87,14 +87,13 @@ const task = new Tasks.RecaptchaV2Proxyless({ websiteURL: 'https://example.com/login', websiteKey: 'YOUR_WEBSITE_KEY' }); -const result = await captchaSolver.solve(task); -console.log(result.gRecaptchaResponse); +captchaSolver.solve(task) + .then((solution) => console.log(solution.gRecaptchaResponse)) + .catch(console.error); ``` -Runnable versions of every example below live in [examples/async](examples/async) (async/await -style) and [examples/sync](examples/sync) (promise-chain style, `.then()/.catch()`) -- both call -the same `CaptchaClient`, since JavaScript has no blocking HTTP client to mirror `requests` vs. -`httpx` the way the Python SDK does. See [Running the examples](#running-the-examples). +Runnable Promise-chain versions of every example below live in [examples](examples). +Each file contains one API operation. See [Running the examples](#running-the-examples). ## Build Faster with AI @@ -142,7 +141,9 @@ const task = new Tasks.GenericTask({ websiteKey: 'YOUR_WEBSITE_KEY' }); -const solution = await captchaSolver.solve(task); +captchaSolver.solve(task) + .then(console.log) + .catch(console.error); ``` `GenericTask` preserves every supplied API field and removes only `null` and `undefined` values. @@ -204,8 +205,7 @@ account's currency. Rejects with `ApiError`, `NetworkError`. Each section below covers one captcha type end-to-end: task fields, the `solution` shape, a runnable example, and a link to the full spec. Optional fields left unset are omitted from the request. Every code block matches a runnable file under -[examples/async](examples/async) (and its [examples/sync](examples/sync) counterpart) --- swap the placeholder `websiteURL`/`websiteKey`/etc. for values from your own +[examples](examples) -- swap the placeholder `websiteURL`/`websiteKey`/etc. for values from your own target page before running. See [Running the examples](#running-the-examples). Types with a `*` counterpart (as opposed to `*Proxyless`) also accept `proxyType` / @@ -242,8 +242,9 @@ const task = new Tasks.RecaptchaV2Proxyless({ websiteURL: 'https://example.com/login', websiteKey: 'YOUR_WEBSITE_KEY' }); -const result = await captchaSolver.solve(task); -console.log(result.gRecaptchaResponse); +captchaSolver.solve(task) + .then((solution) => console.log(solution.gRecaptchaResponse)) + .catch(console.error); ``` With proxy, use `Tasks.RecaptchaV2` instead: @@ -286,8 +287,9 @@ const task = new Tasks.RecaptchaV2EnterpriseProxyless({ websiteURL: 'https://example.com/login', websiteKey: 'YOUR_WEBSITE_KEY' }); -const result = await captchaSolver.solve(task); -console.log(result.gRecaptchaResponse); +captchaSolver.solve(task) + .then((solution) => console.log(solution.gRecaptchaResponse)) + .catch(console.error); ``` With proxy, use `Tasks.RecaptchaV2Enterprise` (same proxy fields as reCAPTCHA v2). @@ -324,8 +326,9 @@ const task = new Tasks.RecaptchaV3Proxyless({ pageAction: 'homepage' }); -const result = await captchaSolver.solve(task); -console.log(result.gRecaptchaResponse); +captchaSolver.solve(task) + .then((solution) => console.log(solution.gRecaptchaResponse)) + .catch(console.error); ``` ### Cloudflare Turnstile @@ -363,8 +366,9 @@ const task = new Tasks.TurnstileProxyless({ websiteURL: 'https://example.com/login', websiteKey: 'YOUR_WEBSITE_KEY' }); -const result = await captchaSolver.solve(task); -console.log(result.token); +captchaSolver.solve(task) + .then((solution) => console.log(solution.token)) + .catch(console.error); ``` With proxy, use `Tasks.Turnstile` (same proxy fields as reCAPTCHA v2). @@ -405,8 +409,9 @@ const task = new Tasks.ImageToText({ minLength: 4, maxLength: 6 }); -const result = await captchaSolver.solve(task); -console.log(result.text); +captchaSolver.solve(task) + .then((solution) => console.log(solution.text)) + .catch(console.error); ``` ### GeeTest (v3 & v4) @@ -448,12 +453,12 @@ const task = new Tasks.GeeTestProxyless({ gt: 'f2ae6cadcf7886856696c46d84d109d1', challenge: '12345678abc90123d45678e90123f45g6' // dynamic -- fetch a fresh one per request }); -const result = await captchaSolver.solve(task); -console.log(result.validate); -console.log(result.seccode); +captchaSolver.solve(task) + .then((solution) => console.log(solution.validate, solution.seccode)) + .catch(console.error); ``` `challenge` is session-specific and expires quickly, so it can't be hardcoded into a -static example -- see [examples/async/geetest_v3.js](examples/async/geetest_v3.js) for +static example -- see [examples/geetest_v3.js](examples/geetest_v3.js) for where the fetch belongs in the flow. ```javascript @@ -462,8 +467,9 @@ const task = new Tasks.GeeTestProxyless({ version: 4, initParameters: { captcha_id: 'YOUR_CAPTCHA_ID' } }); -const result = await captchaSolver.solve(task); -console.log(result.captcha_output); +captchaSolver.solve(task) + .then((solution) => console.log(solution.captcha_output)) + .catch(console.error); ``` With proxy, use `Tasks.GeeTest` (same proxy fields as reCAPTCHA v2). @@ -499,8 +505,9 @@ const task = new Tasks.YandexSmartCaptchaTaskProxyless({ websiteURL: 'https://example.com/login', websiteKey: 'YOUR_WEBSITE_KEY' }); -const result = await captchaSolver.solve(task); -console.log(result.token); +captchaSolver.solve(task) + .then((solution) => console.log(solution.token)) + .catch(console.error); ``` With proxy, use `Tasks.YandexSmartCaptchaTask` (same proxy fields as reCAPTCHA v2, @@ -536,11 +543,11 @@ const task = new Tasks.CoordinatesTask({ body: body, comment: 'click on the green apple' }); -const result = await captchaSolver.solve(task); -console.log(result.coordinates); // [{ x: 358, y: 268 }] +captchaSolver.solve(task) + .then((solution) => console.log(solution.coordinates)) + .catch(console.error); ``` -mode" block in [examples/async/coordinates.js](examples/async/coordinates.js) (or -[examples/sync/coordinates.js](examples/sync/coordinates.js)). +example in [examples/coordinates_traffic_lights.js](examples/coordinates_traffic_lights.js). ### Tencent @@ -573,8 +580,9 @@ const task = new Tasks.TencentTaskProxyless({ websiteURL: 'https://example.com/register', appId: 'YOUR_APP_ID' }); -const result = await captchaSolver.solve(task); -console.log(result.ticket); +captchaSolver.solve(task) + .then((solution) => console.log(solution.ticket)) + .catch(console.error); ``` With proxy, use `Tasks.TencentTask` (same proxy fields as reCAPTCHA v2). @@ -586,8 +594,9 @@ With proxy, use `Tasks.TencentTask` (same proxy fields as reCAPTCHA v2). ```javascript import { CaptchaClient } from '@captcha-solver-api/javascript-sdk'; const captchaSolver = new CaptchaClient({ clientKey: 'your_api_key' }); -const balance = await captchaSolver.getBalance(); -console.log(`Balance: ${balance}`); +captchaSolver.getBalance() + .then((balance) => console.log(`Balance: ${balance}`)) + .catch(console.error); ``` ### Custom timeout and polling @@ -606,7 +615,9 @@ Pass `languagePool` as the second argument to `solve()` (or `createTask()`) to p worker pool by language. Accepted values: `'en'` or `'ru'`. ```javascript -const result = await captchaSolver.solve(task, 'en'); +captchaSolver.solve(task, 'en') + .then(console.log) + .catch(console.error); ``` ### Solving multiple captchas in parallel @@ -621,7 +632,9 @@ const captchaSolver = new CaptchaClient({ clientKey: 'your_api_key' }); const task1 = captchaSolver.solve(new Tasks.RecaptchaV2Proxyless({ websiteURL: 'https://site1.com', websiteKey: 'key1' })); const task2 = captchaSolver.solve(new Tasks.TurnstileProxyless({ websiteURL: 'https://site2.com', websiteKey: 'key2' })); -const results = await Promise.allSettled([task1, task2]); +Promise.allSettled([task1, task2]) + .then(console.log) + .catch(console.error); ``` This completes in roughly the time of the slowest single captcha, not the sum of all of them. @@ -630,9 +643,7 @@ This completes in roughly the time of the slowest single captcha, not the sum of ```javascript import { CaptchaClient, ApiError, TimeoutError, NetworkError, ValidationError } from '@captcha-solver-api/javascript-sdk'; const captchaSolver = new CaptchaClient({ clientKey: 'your_api_key' }); -try { - const result = await captchaSolver.solve(task); -} catch (error) { +captchaSolver.solve(task).catch((error) => { if (error instanceof ValidationError) { console.log(`Invalid input: ${error.message}`); } else if (error instanceof ApiError) { @@ -642,7 +653,7 @@ try { } else if (error instanceof NetworkError) { console.log(`Network error: ${error.message}`); } -} +}); ``` ### TypeScript @@ -657,12 +668,13 @@ import type { TurnstileSolution } from '@captcha-solver-api/javascript-sdk'; const captchaSolver = new CaptchaClient({ clientKey: 'your_api_key' }); -const result = await captchaSolver.solve(new Tasks.TurnstileProxyless({ +captchaSolver.solve(new Tasks.TurnstileProxyless({ websiteURL: 'https://example.com/login', websiteKey: 'YOUR_WEBSITE_KEY' -})); -result.token; // string -result.userAgent; // string +})).then((result) => { + result.token; // string + result.userAgent; // string +}); // The same shape is exported by name if you need to pass it around. const stored: TurnstileSolution = result; @@ -696,9 +708,9 @@ matches this README. ```bash export CAPTCHA_API_KEY=your_api_key -node examples/async/balance.js -node examples/async/image_to_text.js -node examples/async/coordinates.js +node examples/balance.js +node examples/image_to_text.js +node examples/coordinates.js ``` Full details, an example index, and a per-type breakdown of what each script diff --git a/examples/README.md b/examples/README.md index 243ab4a..ebdfe44 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,191 +1,54 @@ # Examples -Runnable scripts for every captcha type the SDK supports. Each file is standalone: it builds a -`CaptchaClient`, creates one or two tasks, waits for the solution and prints it. Nothing here is -imported by the library itself — the directory exists to be read and executed. - -The same 11 scenarios are provided twice, once per calling style: - -| Suite | Style | Look here if | -|---|---|---| -| [async/](async) | `async/await` | your code is `await`-based (the common case) | -| [sync/](sync) | `.then()/.catch()` | you chain promises instead of awaiting them | - -Both suites call the same `CaptchaClient`. The SDK exposes a single promise-based API, and the split -only demonstrates two ways of consuming it — JavaScript has no blocking HTTP client, so there is no -true synchronous variant the way the Python SDK has `requests` vs. `httpx`. - -## Table of contents - -- [Setup](#setup) -- [Running an example](#running-an-example) -- [Example index](#example-index) -- [What each example covers](#what-each-example-covers) -- [Before you run](#before-you-run) +Every file is a standalone Promise-chain example and contains exactly one API +operation. Examples with a customer proxy use the `_proxy.js` suffix. ## Setup -**1. Install dependencies.** The scripts use `dotenv`, which is a devDependency of the repo: +Install dependencies and provide your API key: ```bash npm install ``` -**2. Provide an API key.** Copy the template and fill in your key: - -```bash -cp .env.example .env -``` - -``` +```text CAPTCHA_API_KEY=your_api_key_here ``` -Every script starts with `import 'dotenv/config'` and reads `process.env.CAPTCHA_API_KEY`, falling -back to the literal `'YOUR_API_KEY'` when the variable is missing. That fallback keeps the script -importable, but the API will reject it — set a real key before expecting results. - -**3. Node.js 18 or newer.** The examples rely on global `fetch` and, in `async/`, on top-level -`await`. - -> The scripts import the SDK by package name, exactly as consumer applications do: -> `import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk';` - -## Running an example - -Run from the repository root: +Run an example from the repository root: ```bash -node examples/async/balance.js -node examples/sync/recaptcha_v2.js +node examples/recaptcha_v2.js ``` -`balance.js` is the one file that works as-is — it only needs a valid key. Every other example ships -with placeholder values (`https://example.com/login`, `6Le-xxxxxxxxx`, …) that you must replace with -values from your own target page. See [Before you run](#before-you-run). - -Most files contain **two independent blocks** — a proxyless one and a with-proxy one — that both run -on execution. Comment out the block you don't need, or the script will submit two tasks and spend -twice the balance. +Replace placeholder URLs, site keys, application IDs, dynamic values, and +proxy credentials before running token CAPTCHA examples. ## Example index -| Captcha type | async | sync | Task classes | Solution fields | -|---|---|---|---|---| -| reCAPTCHA v2 | [recaptcha_v2.js](async/recaptcha_v2.js) | [recaptcha_v2.js](sync/recaptcha_v2.js) | `RecaptchaV2Proxyless`, `RecaptchaV2` | `gRecaptchaResponse` | -| reCAPTCHA v2 Enterprise | [recaptcha_v2_enterprise.js](async/recaptcha_v2_enterprise.js) | [recaptcha_v2_enterprise.js](sync/recaptcha_v2_enterprise.js) | `RecaptchaV2EnterpriseProxyless`, `RecaptchaV2Enterprise` | `gRecaptchaResponse` | -| Cloudflare Turnstile | [turnstile.js](async/turnstile.js) | [turnstile.js](sync/turnstile.js) | `TurnstileProxyless`, `Turnstile` | `token` | -| GeeTest v3 | [geetest_v3.js](async/geetest_v3.js) | [geetest_v3.js](sync/geetest_v3.js) | `GeeTestProxyless`, `GeeTest` | `challenge`, `validate`, `seccode` | -| GeeTest v4 | [geetest_v4.js](async/geetest_v4.js) | [geetest_v4.js](sync/geetest_v4.js) | `GeeTestProxyless`, `GeeTest` | `captcha_id`, `lot_number`, `pass_token`, `gen_time`, `captcha_output` | -| Yandex SmartCaptcha | [yandex_smartcaptcha.js](async/yandex_smartcaptcha.js) | [yandex_smartcaptcha.js](sync/yandex_smartcaptcha.js) | `YandexSmartCaptchaTaskProxyless`, `YandexSmartCaptchaTask` | `token` | -| Tencent | [tencent.js](async/tencent.js) | [tencent.js](sync/tencent.js) | `TencentTaskProxyless`, `TencentTask` | `appid`, `ret`, `ticket`, `randstr` | -| Image to Text | [image_to_text.js](async/image_to_text.js) | [image_to_text.js](sync/image_to_text.js) | `ImageToText` | `text` | -| Coordinates (click) | [coordinates.js](async/coordinates.js) | [coordinates.js](sync/coordinates.js) | `CoordinatesTask` | `coordinates` | -| Account balance | [balance.js](async/balance.js) | [balance.js](sync/balance.js) | — (`getBalance()`) | number | - -## What each example covers - -### reCAPTCHA v2 - -The baseline example, and the best one to read first. Shows the two shapes every proxy-capable type -follows: `RecaptchaV2Proxyless` (the service uses its own IPs) and `RecaptchaV2` with the five proxy -fields — `proxyType`, `proxyAddress`, `proxyPort`, `proxyLogin`, `proxyPassword`. Also documents the -two optional client settings, `timeout` and `pollingInterval`, and the `isInvisible` flag for -invisible widgets. - -### reCAPTCHA v2 Enterprise - -Same two shapes as v2, with `enterprisePayload` added. If the target site passes extra parameters to -`grecaptcha.enterprise.render()`, they must be forwarded in that object — otherwise the returned -token is rejected by the site even though the API call succeeded. - - -### Cloudflare Turnstile - -Proxyless and with-proxy variants. The important detail is in the comments: there's no `userAgent` -input for this type -- the worker picks its own while solving and returns it as `result.userAgent` -instead, and the token is tied to that fingerprint, so you must submit it with that exact User-Agent. -For Cloudflare Challenge pages, `action`, `data` (the `data-cdata` attribute) and `pagedata` (the -`chlPageData` parameter -- lowercase, unlike every other field here) also have to be extracted from -the page and passed along. - -### GeeTest v3 - -The only example that performs a **request of its own before solving**. The `challenge` value is -session-specific and must be fresh for every task, so the script fetches one first and then builds -the task. The fetch URL is a deliberate placeholder — see [Before you run](#before-you-run). Uses -`timeout: 300000`, since GeeTest is among the slowest types. `version` -is omitted because v3 is the default. - -### GeeTest v4 - -Same task classes as v3, different identification: v4 drops `gt`/`challenge` entirely and identifies -the widget by `captcha_id` inside `initParameters`, with `version: 4` set explicitly. Solution shape -differs from v3 as well — `captcha_output` and friends instead of `validate`/`seccode`. - -### Yandex SmartCaptcha - -Covers the **token** challenge, proxyless and with proxy. `websiteKey` is the sitekey from the page -a different task type — it lives in the coordinates example below. - -### Tencent - -Proxyless and with proxy. `appId` is read from the page source. `captchaScript` is only needed when -the site loads the widget from a non-default script URL. - -### Image to Text - -Three blocks, no proxy variant, all active. *Basic* submits the shipped letters-only sample -(`text-captcha.png`) with matching character-set hints. *Advanced* solves a different image entirely -— a real math captcha (`captcha-math.png`) with its own instruction image (`captcha-math-instructions.png`) -— using the rest of the hints that speed up recognition: `phrase`, `math`, `comment`, -`imgInstructions`. *With language pool* shows that `languagePool` is the **second argument to -`solve()`**, not a task field: `solve(task, 'en')` picks an English-speaking worker pool (`'en'` or -`'ru'`). - -### Coordinates (click captcha) - -Three blocks, no proxy variant, all active. *Basic* passes the shipped grid captcha -(`coordinates-captcha.png`) plus a `comment` telling the worker what to click — it repeats the -instruction printed on the image itself, since nothing guarantees the worker reads that. *Advanced* -solves a different image — real traffic-lights photo (`traffic-lights.png`) with its own instruction -image (`traffic-lights-instructions.png`) and `minClicks`/`maxClicks` limits. *Yandex SmartCaptcha -token challenge. - -### Account balance - -The shortest script: one `getBalance()` call returning the available amount as a number. It needs no -target page and no placeholder edits, which makes it the fastest way to confirm that your key and -network access work. - -## Before you run - -Every example except `balance.js` needs something replaced first. - -| Example | What you must supply | -|---|---| -| [`balance.js`](async/balance.js) | Nothing — runs as-is with a valid key | -| [`turnstile.js`](async/turnstile.js) | Real `websiteURL` and `websiteKey`; for Challenge pages also `action`, `data`, `pagedata` | -| [`yandex_smartcaptcha.js`](async/yandex_smartcaptcha.js) | Real `websiteURL` and `websiteKey` | -| [`tencent.js`](async/tencent.js) | Real `websiteURL` and `appId` | -| [`geetest_v3.js`](async/geetest_v3.js) | Real `websiteURL` and `gt`, **plus a real init endpoint** (see below) | -| [`geetest_v4.js`](async/geetest_v4.js) | Real `websiteURL` and `captcha_id` | -| [`image_to_text.js`](async/image_to_text.js) | Nothing — all sample images ship in [assets/](assets/) | -| [`coordinates.js`](async/coordinates.js) | Nothing — all sample images ship in [assets/](assets/) | - -**Both image examples run entirely as-is**, with a valid key and nothing else — every block, -[assets/](assets/) and are read through `new URL('../assets/…', import.meta.url)`, which resolves -against the script rather than the working directory — so `node examples/async/coordinates.js` works -from the repository root just as well as from anywhere else. Point the read at your own file to solve -a different image. - -**`geetest_v3.js` will not run end-to-end as-is.** It fetches -`https://target-site.com/path/to/geetest/init`, which is a placeholder, not a live endpoint; the -script exits with code 1 when that fetch fails. Point it at your real target page — or wherever it -exposes a fresh `challenge` — before running. The block is there to show *where* that fetch belongs -in the flow. - -Proxy credentials (`1.2.3.4:8080`, `user`/`password`) are placeholders too. If you only want the -proxyless path, comment out the with-proxy block rather than leaving it to fail. - -Full API reference: https://captcha-solver.com/en/docs/captcha-types +| CAPTCHA type | Without customer proxy | With customer proxy | +|---|---|---| +| reCAPTCHA v2 | [`recaptcha_v2.js`](recaptcha_v2.js) | [`recaptcha_v2_proxy.js`](recaptcha_v2_proxy.js) | +| reCAPTCHA v2 Enterprise | [`recaptcha_v2_enterprise.js`](recaptcha_v2_enterprise.js) | [`recaptcha_v2_enterprise_proxy.js`](recaptcha_v2_enterprise_proxy.js) | +| reCAPTCHA v3 | [`recaptcha_v3.js`](recaptcha_v3.js) | No separate proxy task | +| Cloudflare Turnstile | [`turnstile.js`](turnstile.js) | [`turnstile_proxy.js`](turnstile_proxy.js) | +| GeeTest v3 | [`geetest_v3.js`](geetest_v3.js) | [`geetest_v3_proxy.js`](geetest_v3_proxy.js) | +| GeeTest v4 | [`geetest_v4.js`](geetest_v4.js) | [`geetest_v4_proxy.js`](geetest_v4_proxy.js) | +| Yandex SmartCaptcha | [`yandex_smartcaptcha.js`](yandex_smartcaptcha.js) | [`yandex_smartcaptcha_proxy.js`](yandex_smartcaptcha_proxy.js) | +| Tencent | [`tencent.js`](tencent.js) | [`tencent_proxy.js`](tencent_proxy.js) | +| Image to Text | [`image_to_text.js`](image_to_text.js) | No separate proxy task | +| Coordinates | [`coordinates.js`](coordinates.js) | No separate proxy task | +| Account balance | [`balance.js`](balance.js) | Not applicable | + +Additional one-scenario examples: + +- [`image_to_text_math.js`](image_to_text_math.js) — math CAPTCHA with an instruction image. +- [`image_to_text_language_pool.js`](image_to_text_language_pool.js) — select the English worker pool. +- [`coordinates_traffic_lights.js`](coordinates_traffic_lights.js) — click coordinates with an instruction image. + +> **Important:** `geetest_v3.js` and `geetest_v3_proxy.js` contain a placeholder +> initialization endpoint. Replace it with the target website's endpoint and +> fetch a fresh `challenge` for every task. + +> **Important:** Proxy examples contain placeholder proxy details. Replace all +> of them with your own proxy settings before running a script. diff --git a/examples/async/README.md b/examples/async/README.md deleted file mode 100644 index afb2eeb..0000000 --- a/examples/async/README.md +++ /dev/null @@ -1,153 +0,0 @@ -# Examples — async/await - -The 11 SDK scenarios written in `async/await` style. Every file uses **top-level `await`** (possible -because the package is ESM, `"type": "module"`) and wraps each solve in its own `try/catch`, so a -failing block prints the error and the next block still runs. - -Setup, API key and the general gotchas are documented once in the [parent README](../README.md) — -read that first if you haven't. The identical scenarios in promise-chain style live in -[../sync/](../sync). - -Because every block is awaited at the top level, the blocks in a file execute **sequentially**: a -file with a proxyless block and a with-proxy block submits two tasks, one after the other, and -spends balance twice. Comment out the one you don't need. - -## Table of contents - -- [File index](#file-index) -- [What each file does](#what-each-file-does) - -## File index - -| File | What it does | Blocks in the file | -|---|---|---| -| [balance.js](balance.js) | Reads the account balance | single call | -| [recaptcha_v2.js](recaptcha_v2.js) | Solves reCAPTCHA v2 | Proxyless · With proxy | -| [recaptcha_v2_enterprise.js](recaptcha_v2_enterprise.js) | Solves reCAPTCHA v2 Enterprise | Proxyless · With proxy | -| [turnstile.js](turnstile.js) | Solves Cloudflare Turnstile | Proxyless · With proxy | -| [geetest_v3.js](geetest_v3.js) | Fetches a fresh `challenge`, then solves GeeTest v3 | Fetch · Proxyless · With proxy | -| [geetest_v4.js](geetest_v4.js) | Solves GeeTest v4 via `captcha_id` | Proxyless · With proxy | -| [yandex_smartcaptcha.js](yandex_smartcaptcha.js) | Solves Yandex SmartCaptcha (token challenge) | Proxyless · With proxy | -| [tencent.js](tencent.js) | Solves a Tencent captcha | Proxyless · With proxy | -| [image_to_text.js](image_to_text.js) | Recognises text on a captcha image | Basic · Advanced · With language pool | - -## What each file does - -### balance.js - -[Source code](balance.js) · [API documentation](https://captcha-solver.com/en/docs/methods#post-getbalance) - -```javascript -const balance = await captchaSolver.getBalance(); -``` - -One call, returning the available amount as a number. No task, no target page, no placeholders — -the only file here that runs correctly without editing anything, which makes it the quickest check -that your key works. - -### recaptcha_v2.js - -[Source code](recaptcha_v2.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#recaptcha-v2) - -The reference file for the proxyless/with-proxy pattern that most other examples repeat. -`RecaptchaV2Proxyless` solves through the service's own IPs; `RecaptchaV2` adds `proxyType`, -`proxyAddress`, `proxyPort`, `proxyLogin` and `proxyPassword`. The client constructor is annotated -with the two optional settings — `timeout` (default 120000 ms) and `pollingInterval` (default -5000 ms) — and the task shows `isInvisible` for invisible widgets. Solution: `gRecaptchaResponse`. - -### recaptcha_v2_enterprise.js - -[Source code](recaptcha_v2_enterprise.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#recaptcha-v2-enterprise) - -Same two blocks, plus `enterprisePayload`. Enterprise widgets are rendered through -`grecaptcha.enterprise.render()`, and any extra parameters the site passes there must be forwarded -in that object — omit them and the site rejects an otherwise valid token. Solution: -`gRecaptchaResponse`. - - - -A single block: v3 has no with-proxy variant. `minScore` is required and controls how hard the task -is — `0.3` fastest, `0.7` balanced, `0.9` highest and slowest — so the client is created with -`timeout: 180000`. `pageAction` should mirror the action the site sets in `grecaptcha.execute()`. -Commented-out lines show `isEnterprise` and `apiDomain` for sites loading from `recaptcha.net`. -Solution: `gRecaptchaResponse`. - -### turnstile.js - -[Source code](turnstile.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#cloudflare-turnstile) - -Proxyless and with-proxy blocks. The commented optional fields matter more here than elsewhere: -`action`, `data` (`data-cdata`) and `pagedata` (`chlPageData`, lowercase unlike the rest) are required for Cloudflare Challenge -pages. Note the User-Agent rule spelled out in the comments — there's no `userAgent` input field; -the worker picks its own and returns it as `result.userAgent`, and the browser or bot submitting -the token must send that exact one back. Solution: `token` (plus `userAgent`). - -### geetest_v3.js - -[Source code](geetest_v3.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#geetest-v3) - -The only file that makes a request of its own before solving. `challenge` is session-specific and -must be fresh per task, so the script awaits a `fetch` to the target's init endpoint, destructures -`challenge` out of the JSON, and only then builds the task; if that fetch throws, it logs and calls -`process.exit(1)`. - -That URL — `https://target-site.com/path/to/geetest/init` — is a **placeholder**, so the file does -not run end-to-end unmodified. It marks where the fetch belongs in the flow; point it at your real -source of `challenge` first. - -The client uses `timeout: 300000`, GeeTest being among the slowest -types. `version` is omitted since v3 is the default. Solution: `challenge`, `validate`, `seccode`. - -### geetest_v4.js - -[Source code](geetest_v4.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#geetest-v4) - -Same `GeeTestProxyless`/`GeeTest` classes as v3, but v4 identifies the widget differently: no -`gt`, no `challenge`, instead `version: 4` and `captcha_id` inside `initParameters`. No pre-fetch is -needed. Same extended timeouts. Solution: `captcha_id`, `lot_number`, `pass_token`, `gen_time`, -`captcha_output`. - -### yandex_smartcaptcha.js - -[Source code](yandex_smartcaptcha.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#yandex-smartcaptcha) - -The **token** challenge, proxyless and with proxy. `websiteKey` is the sitekey from the page source -[coordinates.js](coordinates.js) instead — different task class entirely. Solution: `token`. - -### tencent.js - -[Source code](tencent.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#tencent) - -Proxyless and with proxy. `appId` comes from the page source; `captchaScript` is only needed when -the site loads the widget from a non-default script URL. Solution: `appid`, `ret`, `ticket`, -`randstr`. - -### image_to_text.js - -[Source code](image_to_text.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#image-to-text) - -Three blocks, no proxy variant, all active. *Basic* sends the shipped `text-captcha.png` sample with -the character-set hints that match it — `numeric: 2` (letters), `minLength`/`maxLength`. *Advanced* -solves a different image entirely, `captcha-math.png`, using the rest of the hints — `phrase`, -`math`, `comment`, and `captcha-math-instructions.png` passed as `imgInstructions`. *With language -pool* demonstrates the one API detail that is easy to get wrong: `languagePool` is the **second -argument to `solve()`**, not a task field — `await captchaSolver.solve(task, 'en')`, accepting `'en'` -or `'ru'`. - -Reads its images via `new URL(..., import.meta.url)`, so the path holds **regardless of the working -directory** and the script runs as-is. Solution: `text`. - -### coordinates.js - -[Source code](coordinates.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#coordinates) - -Three blocks, no proxy variant, all active. *Basic* submits the shipped grid captcha -(`coordinates-captcha.png`) plus a `comment` telling the worker what to click. *Advanced* solves a -different image, a real traffic-lights photo (`traffic-lights.png`), with -`traffic-lights-instructions.png` as `imgInstructions` and `minClicks`/`maxClicks` limits. *Yandex -SmartCaptcha screenshot (`yandex-smartcaptcha-sample.jpg`) whose click-order hint is baked into the -image itself, so no separate `imgInstructions` is passed — this is how the image variant of Yandex -SmartCaptcha is solved. - -Reads its images via `new URL(..., import.meta.url)`, so the path holds **regardless of the working -directory** and the script runs as-is. Solution: `coordinates`, an array of `{ x, y }` points. diff --git a/examples/async/balance.js b/examples/async/balance.js deleted file mode 100644 index dcc6e05..0000000 --- a/examples/async/balance.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Example: Get account balance. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Returns the current available balance of your account. - */ - -import 'dotenv/config'; -import { CaptchaClient } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// Get the current account balance. -// Returns a float with the available amount in your account currency. -try { - const balance = await captchaSolver.getBalance(); - console.log('Balance:', balance); -} catch (error) { - console.error(error); -} diff --git a/examples/async/coordinates.js b/examples/async/coordinates.js deleted file mode 100644 index 4e91a30..0000000 --- a/examples/async/coordinates.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Example: Solve a click-based image captcha using CoordinatesTask. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. That is all: the sample - * captcha ships with the repository, in examples/assets/. - * Point the read below at your own file to solve a different image, and use - * comment to tell the worker what to click on it. - * No proxy is required. The image is submitted directly to the service. - */ - -import 'dotenv/config'; -import fs from 'fs'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// Read and encode the captcha image to base64. -// The body must be a pure base64 string without the data:image/...;base64, prefix. -// The path is resolved against this file rather than the working directory, so -// the example runs from anywhere -- including from the repository root. -const body = fs.readFileSync(new URL('../assets/coordinates-captcha.png', import.meta.url)).toString('base64'); - -// --- Basic example --- -// Solves a simple click-based captcha with a hint for the worker. The comment -// repeats the instruction printed on the sample image, because nothing -// guarantees the worker reads the text baked into the picture. -try { - const task = new Tasks.CoordinatesTask({ - body: body, // Base64-encoded captcha image (required) - comment: 'click on all squares with street signs' // Text hint for the worker - }); - const result = await captchaSolver.solve(task); - // Solution contains { coordinates: [{ x: 358, y: 268 }] } - console.log('result:', result); -} catch (error) { - console.error(error); -} - -// --- Advanced example --- -// Solves a captcha (examples/assets/traffic-lights.png) with instruction image -// (examples/assets/traffic-lights-instructions.png) and click count limits. -// Uses its own body -- the basic example's sample image above isn't traffic lights. -try { - const trafficBody = fs.readFileSync(new URL('../assets/traffic-lights.png', import.meta.url)).toString('base64'); - const imgInstructions = fs.readFileSync( - new URL('../assets/traffic-lights-instructions.png', import.meta.url) - ).toString('base64'); - - const task = new Tasks.CoordinatesTask({ - body: trafficBody, // Base64-encoded captcha image - comment: 'click on all traffic lights', // Text hint for the worker - imgInstructions: imgInstructions, // Optional instruction image - minClicks: 1, // Minimum number of clicks (default 1) - maxClicks: 3 // Maximum number of clicks allowed - }); - const result = await captchaSolver.solve(task); - console.log('result:', result); -} catch (error) { - console.error(error); -} diff --git a/examples/async/geetest_v3.js b/examples/async/geetest_v3.js deleted file mode 100644 index f8a51d9..0000000 --- a/examples/async/geetest_v3.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Example: Solve a GeeTest v3 challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL, gt, and challenge with values from your target page. - * Important: the challenge value is dynamic. Fetch a fresh one for each request. - * - * NOTE: "https://target-site.com/path/to/geetest/init" below is a PLACEHOLDER, not a - * real endpoint -- this script will not run end-to-end as-is. Replace it with a request - * to your actual target page (or wherever it exposes a fresh `challenge` value) before - * running this example. It's here only to illustrate where that fetch belongs in the flow. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -// GeeTest tasks may take longer. Increase timeout if needed. -const captchaSolver = new CaptchaClient({ clientKey: apiKey, timeout: 300000 }); - -// Fetch a fresh challenge value from the target page. -// In production, extract this from the page's initGeetest call or network requests. -// "target-site.com" is a placeholder -- point this at your real target before running. -let challenge; -try { - const initResponse = await fetch('https://target-site.com/path/to/geetest/init'); - ({ challenge } = await initResponse.json()); -} catch (error) { - console.error('Failed to fetch a fresh challenge value:', error); - process.exit(1); -} - -// --- Proxyless example --- -// v3 is the default version, so the version field can be omitted. -try { - const task = new Tasks.GeeTestProxyless({ - websiteURL: 'https://example.com/login', // Full URL of the page with GeeTest - gt: 'f2ae6cadcf7886856696c46d84d109d1', // Public key of the GeeTest widget - challenge: challenge // Session-specific value, must be fresh - // Optional fields - // geetestApiServerSubdomain: 'api-na.geetest.com', // Custom API subdomain - // initParameters: {...}, // Extra params from initGeetest call - // risk_type: 'slide', // Dynamic value from the page's captcha-loading request, if present (note: snake_case, not riskType) - // userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...', - }); - const result = await captchaSolver.solve(task); - // Solution contains { challenge, validate, seccode } - console.log('result:', result); -} catch (error) { - console.error(error); -} - -// --- With proxy example --- -try { - const task = new Tasks.GeeTest({ - websiteURL: 'https://example.com/login', - gt: 'f2ae6cadcf7886856696c46d84d109d1', - challenge: challenge, - proxyType: 'http', // http, socks4, or socks5 - proxyAddress: '1.2.3.4', // Proxy IP address - proxyPort: 8080, // Proxy port - proxyLogin: 'user', // Login for proxy authorization (optional) - proxyPassword: 'password' // Password for proxy authorization (optional) - }); - const result = await captchaSolver.solve(task); - console.log('result:', result); -} catch (error) { - console.error(error); -} diff --git a/examples/async/geetest_v4.js b/examples/async/geetest_v4.js deleted file mode 100644 index ffcf4b9..0000000 --- a/examples/async/geetest_v4.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Example: Solve a GeeTest v4 challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL and captcha_id with values from your target page. - * GeeTest v4 drops gt/challenge entirely. It uses captcha_id instead. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -// GeeTest v4 tasks may take longer. Increase timeout if needed. -const captchaSolver = new CaptchaClient({ clientKey: apiKey, timeout: 300000 }); - -// --- Proxyless example --- -// v4 drops gt/challenge. The widget is identified by captcha_id inside initParameters. -try { - const task = new Tasks.GeeTestProxyless({ - websiteURL: 'https://example.com/login', // Full URL of the page with GeeTest v4 - version: 4, // Required: must be 4 for this version - initParameters: { // Required: must contain captcha_id - captcha_id: 'e392e1d7fd421dc63325744d5a2b9c73' // Static site identifier - } - // Optional fields - // risk_type: 'slide', // Dynamic value from the page's captcha-loading request, if present (note: snake_case, not riskType) - // userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...', - }); - const result = await captchaSolver.solve(task); - // Solution contains { captcha_id, lot_number, pass_token, gen_time, captcha_output } - console.log('result:', result); -} catch (error) { - console.error(error); -} - -// --- With proxy example --- -try { - const task = new Tasks.GeeTest({ - websiteURL: 'https://example.com/login', - version: 4, - initParameters: { - captcha_id: 'e392e1d7fd421dc63325744d5a2b9c73' - }, - proxyType: 'http', // http, socks4, or socks5 - proxyAddress: '1.2.3.4', // Proxy IP address - proxyPort: 8080, // Proxy port - proxyLogin: 'user', // Login for proxy authorization (optional) - proxyPassword: 'password' // Password for proxy authorization (optional) - }); - const result = await captchaSolver.solve(task); - console.log('result:', result); -} catch (error) { - console.error(error); -} diff --git a/examples/async/image_to_text.js b/examples/async/image_to_text.js deleted file mode 100644 index dca3672..0000000 --- a/examples/async/image_to_text.js +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Example: Solve an Image to Text challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. That is all: the sample - * captcha ships with the repository, in examples/assets/. - * Point the read below at your own file to solve a different image. - * Use optional fields to give hints to the worker for faster solving. - */ - -import 'dotenv/config'; -import fs from 'fs'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -// Image to Text tasks are usually fast. Default timeout is fine. -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// Read and encode the captcha image to base64. -// The body must be a pure base64 string without the data:image/...;base64, prefix. -// The path is resolved against this file rather than the working directory, so -// the example runs from anywhere -- including from the repository root. -const body = fs.readFileSync(new URL('../assets/text-captcha.png', import.meta.url)).toString('base64'); - -// --- Basic example --- -// Solves a simple image captcha with character set hints. The hints below match -// the sample image, which is letters and no digits -- adjust them for your own. -try { - const task = new Tasks.ImageToText({ - body: body, // Base64-encoded image (required) - numeric: 2, // 2 = letters only - minLength: 4, // Minimum expected answer length - maxLength: 6 // Maximum expected answer length - }); - const result = await captchaSolver.solve(task); - // Solution contains { text: "aB3fX9" } - console.log('result:', result); -} catch (error) { - console.error(error); -} - -// --- Advanced example --- -// Solves a math captcha (examples/assets/captcha-math.png) with comment and -// instruction image (examples/assets/captcha-math-instructions.png). Uses its -// own body -- the basic example's sample image above isn't a math captcha. -try { - const mathBody = fs.readFileSync(new URL('../assets/captcha-math.png', import.meta.url)).toString('base64'); - const imgInstructions = fs.readFileSync( - new URL('../assets/captcha-math-instructions.png', import.meta.url) - ).toString('base64'); - - const task = new Tasks.ImageToText({ - body: mathBody, // Base64-encoded captcha image - // Optional fields (pass only if needed by the captcha type) - phrase: false, // true if answer has multiple words - case_: true, // true if answer is case-sensitive - numeric: 0, // 0 = not specified, 1 = digits, 2 = letters, 3 = any with digits, 4 = any with letters - math: true, // true if image is a math expression to solve - minLength: 1, // Minimum answer length - maxLength: 10, // Maximum answer length - comment: 'Enter the result of the equation', // Text hint for the worker - imgInstructions: imgInstructions // Optional instruction image for the worker - }); - const result = await captchaSolver.solve(task); - console.log('result:', result); -} catch (error) { - console.error(error); -} - -// --- With language pool --- -// The languagePool parameter selects the worker pool by language. -// Pass it as the second argument to solve(), not inside the task. -// Accepted values: "en" (English) or "ru" (Russian). -try { - const task = new Tasks.ImageToText({ - body: body, - numeric: 2, - minLength: 4, - maxLength: 6 - }); - const result = await captchaSolver.solve(task, 'en'); // Picks English-speaking worker pool - console.log('result:', result); -} catch (error) { - console.error(error); -} diff --git a/examples/async/recaptcha_v2.js b/examples/async/recaptcha_v2.js deleted file mode 100644 index 4254b23..0000000 --- a/examples/async/recaptcha_v2.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Example: Solve a reCAPTCHA v2 challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL and websiteKey with values from your target page. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -// Create a solver instance with your API key. -// Optional: timeout (ms to wait for solution, default 120000) -// Optional: pollingInterval (ms between status checks, default 5000) -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// --- Proxyless example --- -// Solves reCAPTCHA v2 without a proxy. The service uses its own IP addresses. -try { - const task = new Tasks.RecaptchaV2Proxyless({ - websiteURL: 'https://example.com/login', // Full URL of the page with captcha - websiteKey: '6Le-xxxxxxxxxxxxxxxxxxxxxxxxxxxx', // data-sitekey attribute value - isInvisible: false // Set true for invisible reCAPTCHA - }); - const result = await captchaSolver.solve(task); - // Solution contains { gRecaptchaResponse: "03AGdBq..." } - console.log('result:', result); -} catch (error) { - console.error(error); -} - -// --- With proxy example --- -// Solves reCAPTCHA v2 through your own proxy. Required when the target site is -// geo-restricted or you need session consistency. -try { - const task = new Tasks.RecaptchaV2({ - websiteURL: 'https://example.com/login', - websiteKey: '6Le-xxxxxxxxxxxxxxxxxxxxxxxxxxxx', - // --- Proxy parameters --- - proxyType: 'http', // http, socks4, or socks5 - proxyAddress: '1.2.3.4', // Proxy IP address - proxyPort: 8080, // Proxy port - proxyLogin: 'user', // Login for proxy authorization (optional) - proxyPassword: 'password' // Password for proxy authorization (optional) - }); - const result = await captchaSolver.solve(task); - console.log('result:', result); -} catch (error) { - console.error(error); -} diff --git a/examples/async/recaptcha_v2_enterprise.js b/examples/async/recaptcha_v2_enterprise.js deleted file mode 100644 index 2669ffc..0000000 --- a/examples/async/recaptcha_v2_enterprise.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Example: Solve a reCAPTCHA v2 Enterprise challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL and websiteKey with values from your target page. - * If the site uses enterprisePayload, extract and pass it or the token may be rejected. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// --- Proxyless example --- -// Enterprise captchas are loaded via the reCAPTCHA Enterprise API. If the site -// passes extra parameters to grecaptcha.enterprise.render(), pass them as -// enterprisePayload or the token will be rejected. -try { - const task = new Tasks.RecaptchaV2EnterpriseProxyless({ - websiteURL: 'https://example.com/login', - websiteKey: '6Le-xxxxxxxxxxxxxxxxxxxxxxxxxxxx', - isInvisible: false - // Optional fields (pass only if the target site requires them): - // enterprisePayload: { s: 'value-from-page' }, - // apiDomain: 'recaptcha.net', - // userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...', - // cookies: 'session=abc123; token=xyz789', - }); - const result = await captchaSolver.solve(task); - // Solution contains { gRecaptchaResponse: "03AGdBq..." } - console.log('result:', result); -} catch (error) { - console.error(error); -} - -// --- With proxy example --- -try { - const task = new Tasks.RecaptchaV2Enterprise({ - websiteURL: 'https://example.com/login', - websiteKey: '6Le-xxxxxxxxxxxxxxxxxxxxxxxxxxxx', - proxyType: 'http', - proxyAddress: '1.2.3.4', - proxyPort: 8080, - proxyLogin: 'user', - proxyPassword: 'password', - isInvisible: false - }); - const result = await captchaSolver.solve(task); - console.log('result:', result); -} catch (error) { - console.error(error); -} diff --git a/examples/async/recaptcha_v3.js b/examples/async/recaptcha_v3.js deleted file mode 100644 index 2d2a9a7..0000000 --- a/examples/async/recaptcha_v3.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Example: Solve reCAPTCHA v3 without a customer proxy. - * Set CAPTCHA_API_KEY and replace the page-specific values before running. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const captchaSolver = new CaptchaClient({ - clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' -}); - -const task = new Tasks.RecaptchaV3Proxyless({ - websiteURL: 'https://example.com/login', - websiteKey: 'YOUR_WEBSITE_KEY', - minScore: 0.3, - pageAction: 'homepage' -}); - -try { - const result = await captchaSolver.solve(task); - console.log('result:', result); -} catch (error) { - console.error(error); -} diff --git a/examples/async/tencent.js b/examples/async/tencent.js deleted file mode 100644 index 670026d..0000000 --- a/examples/async/tencent.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Example: Solve a Tencent captcha challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL and appId with values from your target page. - * Pass captchaScript if the site uses a non-default script URL. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// --- Proxyless example --- -// The service's own proxies are used to solve the captcha. -// appId is found in the page source code. captchaScript is optional if the site uses the default. -try { - const task = new Tasks.TencentTaskProxyless({ - websiteURL: 'https://example.com/login', // Full URL of the page with captcha - appId: '190014885' // appId from page source code (required) - // Optional fields: - // captchaScript: 'https://turing.captcha.qcloud.com/TCaptcha.js', - }); - const result = await captchaSolver.solve(task); - // Solution contains { appid, ret, ticket, randstr } - // Pass all four values together into the page's captcha callback as-is. - console.log('result:', result); -} catch (error) { - console.error(error); -} - -// --- With proxy example --- -// Use when the target site is geo-restricted or you need a consistent session. -try { - const task = new Tasks.TencentTask({ - websiteURL: 'https://example.com/login', - appId: '190014885', - // --- Proxy parameters --- - proxyType: 'http', // http, socks4, or socks5 - proxyAddress: '1.2.3.4', // Proxy IP address - proxyPort: 8080, // Proxy port - proxyLogin: 'user', // Login for proxy authorization (optional) - proxyPassword: 'password' // Password for proxy authorization (optional) - }); - const result = await captchaSolver.solve(task); - console.log('result:', result); -} catch (error) { - console.error(error); -} diff --git a/examples/async/turnstile.js b/examples/async/turnstile.js deleted file mode 100644 index 06c0271..0000000 --- a/examples/async/turnstile.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Example: Solve a Cloudflare Turnstile challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL and websiteKey with values from your target page. - * For Cloudflare Challenge pages, also extract and pass action, data, pagedata, - * and the current browser userAgent. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// --- Proxyless example --- -// For a Cloudflare Challenge page, pass the current browser User-Agent together -// with action, data, and pagedata. Do not change the browser User-Agent after solving. -try { - const task = new Tasks.TurnstileProxyless({ - websiteURL: 'https://example.com/login', // Full URL of the page with Turnstile - websiteKey: '0x4AAAAAAAxxxxxxxxxxxxxxxx', // data-sitekey attribute value - // Optional fields (pass only if the target site sets them) - // action: 'login', // Value of data-action attribute - // data: 'custom-cdata-value', // Value of data-cdata attribute - // pagedata: 'chl-page-data-value', // Value of chlPageData parameter - // userAgent: 'Mozilla/5.0 ...', // Current browser User-Agent - }); - const result = await captchaSolver.solve(task); - // Solution contains { token: "0.zxcv...", userAgent: "Mozilla/5.0 ..." } - console.log('result:', result); -} catch (error) { - console.error(error); -} - -// --- With proxy example --- -try { - const task = new Tasks.Turnstile({ - websiteURL: 'https://example.com/login', - websiteKey: '0x4AAAAAAAxxxxxxxxxxxxxxxx', - // --- Proxy parameters --- - proxyType: 'http', // http, socks4, or socks5 - proxyAddress: '1.2.3.4', // Proxy IP address - proxyPort: 8080, // Proxy port - proxyLogin: 'user', // Login for proxy authorization (optional) - proxyPassword: 'password' // Password for proxy authorization (optional) - // --- Optional fields --- - // action: 'login', - // data: 'custom-cdata-value', - // pagedata: 'chl-page-data-value', - }); - const result = await captchaSolver.solve(task); - console.log('result:', result); -} catch (error) { - console.error(error); -} diff --git a/examples/async/yandex_smartcaptcha.js b/examples/async/yandex_smartcaptcha.js deleted file mode 100644 index a937413..0000000 --- a/examples/async/yandex_smartcaptcha.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Example: Solve a Yandex SmartCaptcha challenge (token-based). - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL and websiteKey with values from your target page. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// --- Proxyless example --- -// The service's own proxies are used to solve the captcha. -// websiteKey is the sitekey value from the page code or captcha iframe. -try { - const task = new Tasks.YandexSmartCaptchaTaskProxyless({ - websiteURL: 'https://example.com/login', // Full URL of the page with captcha - websiteKey: 'FEXfAbHQsToo97VidNVk3j4dC74nGW1DgdxK4OoR' // sitekey from page code or iframe - // Optional fields: - // userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...', - // cookies: 'session=abc123; token=xyz789', - }); - const result = await captchaSolver.solve(task); - // Solution contains { token: "dV9xNjYyNTU3NjkxO4k9OTQuNVMuMjkuMjM9..." } - console.log('result:', result); -} catch (error) { - console.error(error); -} - -// --- With proxy example --- -// Note: this is the only captcha type where an https proxy is accepted. -try { - const task = new Tasks.YandexSmartCaptchaTask({ - websiteURL: 'https://example.com/login', - websiteKey: 'FEXfAbHQsToo97VidNVk3j4dC74nGW1DgdxK4OoR', - // --- Proxy parameters --- - proxyType: 'http', // http, https, socks4, or socks5 (https is accepted only for this type) - proxyAddress: '1.2.3.4', // Proxy IP address - proxyPort: 8080, // Proxy port - proxyLogin: 'user', // Login for proxy authorization (optional) - proxyPassword: 'password' // Password for proxy authorization (optional) - }); - const result = await captchaSolver.solve(task); - console.log('result:', result); -} catch (error) { - console.error(error); -} diff --git a/examples/sync/balance.js b/examples/balance.js similarity index 100% rename from examples/sync/balance.js rename to examples/balance.js diff --git a/examples/coordinates.js b/examples/coordinates.js new file mode 100644 index 0000000..fb6c748 --- /dev/null +++ b/examples/coordinates.js @@ -0,0 +1,13 @@ +import 'dotenv/config'; +import fs from 'fs'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.CoordinatesTask({ + body: fs.readFileSync(new URL('./assets/coordinates-captcha.png', import.meta.url)).toString('base64'), + comment: 'Click on the green apple' +}); + +client.solve(task) + .then((solution) => console.log(solution.coordinates)) + .catch(console.error); diff --git a/examples/coordinates_traffic_lights.js b/examples/coordinates_traffic_lights.js new file mode 100644 index 0000000..00ee1ab --- /dev/null +++ b/examples/coordinates_traffic_lights.js @@ -0,0 +1,15 @@ +import 'dotenv/config'; +import fs from 'fs'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.CoordinatesTask({ + body: fs.readFileSync(new URL('./assets/traffic-lights.png', import.meta.url)).toString('base64'), + imgInstructions: fs.readFileSync(new URL('./assets/traffic-lights-instructions.png', import.meta.url)).toString('base64'), + minClicks: 1, + maxClicks: 5 +}); + +client.solve(task) + .then((solution) => console.log(solution.coordinates)) + .catch(console.error); diff --git a/examples/geetest_v3.js b/examples/geetest_v3.js new file mode 100644 index 0000000..12223f2 --- /dev/null +++ b/examples/geetest_v3.js @@ -0,0 +1,14 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY', timeout: 300000 }); + +fetch('https://target-site.com/path/to/geetest/init') + .then((response) => response.json()) + .then(({ challenge }) => client.solve(new Tasks.GeeTestProxyless({ + websiteURL: 'https://example.com/login', + gt: 'YOUR_GT_KEY', + challenge + }))) + .then((solution) => console.log(solution)) + .catch(console.error); diff --git a/examples/geetest_v3_proxy.js b/examples/geetest_v3_proxy.js new file mode 100644 index 0000000..21b4bbc --- /dev/null +++ b/examples/geetest_v3_proxy.js @@ -0,0 +1,17 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY', timeout: 300000 }); + +fetch('https://target-site.com/path/to/geetest/init') + .then((response) => response.json()) + .then(({ challenge }) => client.solve(new Tasks.GeeTest({ + websiteURL: 'https://example.com/login', + gt: 'YOUR_GT_KEY', + challenge, + proxyType: 'http', + proxyAddress: '1.2.3.4', + proxyPort: 8080 + }))) + .then((solution) => console.log(solution)) + .catch(console.error); diff --git a/examples/geetest_v4.js b/examples/geetest_v4.js new file mode 100644 index 0000000..8c1084c --- /dev/null +++ b/examples/geetest_v4.js @@ -0,0 +1,13 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY', timeout: 300000 }); +const task = new Tasks.GeeTestProxyless({ + websiteURL: 'https://example.com/login', + version: 4, + initParameters: { captcha_id: 'YOUR_CAPTCHA_ID' } +}); + +client.solve(task) + .then((solution) => console.log(solution)) + .catch(console.error); diff --git a/examples/geetest_v4_proxy.js b/examples/geetest_v4_proxy.js new file mode 100644 index 0000000..1ff188e --- /dev/null +++ b/examples/geetest_v4_proxy.js @@ -0,0 +1,16 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY', timeout: 300000 }); +const task = new Tasks.GeeTest({ + websiteURL: 'https://example.com/login', + version: 4, + initParameters: { captcha_id: 'YOUR_CAPTCHA_ID' }, + proxyType: 'http', + proxyAddress: '1.2.3.4', + proxyPort: 8080 +}); + +client.solve(task) + .then((solution) => console.log(solution)) + .catch(console.error); diff --git a/examples/image_to_text.js b/examples/image_to_text.js new file mode 100644 index 0000000..ca2bd02 --- /dev/null +++ b/examples/image_to_text.js @@ -0,0 +1,15 @@ +import 'dotenv/config'; +import fs from 'fs'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.ImageToText({ + body: fs.readFileSync(new URL('./assets/text-captcha.png', import.meta.url)).toString('base64'), + numeric: 2, + minLength: 4, + maxLength: 6 +}); + +client.solve(task) + .then((solution) => console.log(solution.text)) + .catch(console.error); diff --git a/examples/image_to_text_language_pool.js b/examples/image_to_text_language_pool.js new file mode 100644 index 0000000..db08a44 --- /dev/null +++ b/examples/image_to_text_language_pool.js @@ -0,0 +1,12 @@ +import 'dotenv/config'; +import fs from 'fs'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.ImageToText({ + body: fs.readFileSync(new URL('./assets/text-captcha.png', import.meta.url)).toString('base64') +}); + +client.solve(task, 'en') + .then((solution) => console.log(solution.text)) + .catch(console.error); diff --git a/examples/image_to_text_math.js b/examples/image_to_text_math.js new file mode 100644 index 0000000..8c7dcaa --- /dev/null +++ b/examples/image_to_text_math.js @@ -0,0 +1,15 @@ +import 'dotenv/config'; +import fs from 'fs'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.ImageToText({ + body: fs.readFileSync(new URL('./assets/captcha-math.png', import.meta.url)).toString('base64'), + math: true, + comment: 'Enter the result of the equation', + imgInstructions: fs.readFileSync(new URL('./assets/captcha-math-instructions.png', import.meta.url)).toString('base64') +}); + +client.solve(task) + .then((solution) => console.log(solution.text)) + .catch(console.error); diff --git a/examples/recaptcha_v2.js b/examples/recaptcha_v2.js new file mode 100644 index 0000000..2e7b01f --- /dev/null +++ b/examples/recaptcha_v2.js @@ -0,0 +1,12 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.RecaptchaV2Proxyless({ + websiteURL: 'https://example.com/login', + websiteKey: 'YOUR_WEBSITE_KEY' +}); + +client.solve(task) + .then((solution) => console.log(solution.gRecaptchaResponse)) + .catch(console.error); diff --git a/examples/recaptcha_v2_enterprise.js b/examples/recaptcha_v2_enterprise.js new file mode 100644 index 0000000..dd7c6e3 --- /dev/null +++ b/examples/recaptcha_v2_enterprise.js @@ -0,0 +1,12 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.RecaptchaV2EnterpriseProxyless({ + websiteURL: 'https://example.com/login', + websiteKey: 'YOUR_WEBSITE_KEY' +}); + +client.solve(task) + .then((solution) => console.log(solution.gRecaptchaResponse)) + .catch(console.error); diff --git a/examples/recaptcha_v2_enterprise_proxy.js b/examples/recaptcha_v2_enterprise_proxy.js new file mode 100644 index 0000000..6817580 --- /dev/null +++ b/examples/recaptcha_v2_enterprise_proxy.js @@ -0,0 +1,15 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.RecaptchaV2Enterprise({ + websiteURL: 'https://example.com/login', + websiteKey: 'YOUR_WEBSITE_KEY', + proxyType: 'http', + proxyAddress: '1.2.3.4', + proxyPort: 8080 +}); + +client.solve(task) + .then((solution) => console.log(solution.gRecaptchaResponse)) + .catch(console.error); diff --git a/examples/recaptcha_v2_proxy.js b/examples/recaptcha_v2_proxy.js new file mode 100644 index 0000000..2dc1745 --- /dev/null +++ b/examples/recaptcha_v2_proxy.js @@ -0,0 +1,17 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.RecaptchaV2({ + websiteURL: 'https://example.com/login', + websiteKey: 'YOUR_WEBSITE_KEY', + proxyType: 'http', + proxyAddress: '1.2.3.4', + proxyPort: 8080, + proxyLogin: 'user', + proxyPassword: 'password' +}); + +client.solve(task) + .then((solution) => console.log(solution.gRecaptchaResponse)) + .catch(console.error); diff --git a/examples/sync/recaptcha_v3.js b/examples/recaptcha_v3.js similarity index 100% rename from examples/sync/recaptcha_v3.js rename to examples/recaptcha_v3.js diff --git a/examples/sync/README.md b/examples/sync/README.md deleted file mode 100644 index 67c29cb..0000000 --- a/examples/sync/README.md +++ /dev/null @@ -1,164 +0,0 @@ -# Examples — promise chains - -The same 11 SDK scenarios as [../async/](../async), written with `.then()/.catch()` instead of -`await`. Nothing about the SDK changes between the two suites: `CaptchaClient` returns promises, and -these files simply consume them without `await`. - -Setup, API key and the general gotchas are documented once in the [parent README](../README.md) — -read that first if you haven't. - -Two consequences of dropping `await` are worth knowing before you run anything here: - -- **Tasks are named, not inline.** Each block declares its task in a `const` (`proxylessTask`, - `proxyTask`, `basicTask`, …) and then passes it to `captchaSolver.solve(...)`, which keeps the - promise chain readable. -- **Blocks start concurrently.** In `async/`, a top-level `await` makes the proxyless block finish - before the with-proxy block begins. Here nothing blocks, so both `solve()` calls are submitted - almost simultaneously and their results arrive in whatever order the API returns them. Two tasks, - twice the balance — comment out the block you don't need. - -Errors are handled per chain with a trailing `.catch()`; a failing block does not stop the others. - -## Table of contents - -- [File index](#file-index) -- [What each file does](#what-each-file-does) - -## File index - -| File | What it does | Blocks in the file | -|---|---|---| -| [balance.js](balance.js) | Reads the account balance | single call | -| [recaptcha_v2.js](recaptcha_v2.js) | Solves reCAPTCHA v2 | Proxyless · With proxy | -| [recaptcha_v2_enterprise.js](recaptcha_v2_enterprise.js) | Solves reCAPTCHA v2 Enterprise | Proxyless · With proxy | -| [turnstile.js](turnstile.js) | Solves Cloudflare Turnstile | Proxyless · With proxy | -| [geetest_v3.js](geetest_v3.js) | Fetches a fresh `challenge`, then solves GeeTest v3 | one nested chain | -| [geetest_v4.js](geetest_v4.js) | Solves GeeTest v4 via `captcha_id` | Proxyless · With proxy | -| [yandex_smartcaptcha.js](yandex_smartcaptcha.js) | Solves Yandex SmartCaptcha (token challenge) | Proxyless · With proxy | -| [tencent.js](tencent.js) | Solves a Tencent captcha | Proxyless · With proxy | -| [image_to_text.js](image_to_text.js) | Recognises text on a captcha image | Basic · Advanced · With language pool | - -## What each file does - -### balance.js - -[Source code](balance.js) · [API documentation](https://captcha-solver.com/en/docs/methods#post-getbalance) - -```javascript -captchaSolver.getBalance() - .then((balance) => console.log('Balance:', balance)) -``` - -One call resolving to the available amount as a number. No task, no target page, no placeholders — -the only file here that runs correctly without editing anything, and therefore the quickest check -that your key works. - -### recaptcha_v2.js - -[Source code](recaptcha_v2.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#recaptcha-v2) - -The reference file for the proxyless/with-proxy pattern most other examples repeat. -`RecaptchaV2Proxyless` solves through the service's own IPs; `RecaptchaV2` adds `proxyType`, -`proxyAddress`, `proxyPort`, `proxyLogin` and `proxyPassword`. The client constructor is annotated -with the two optional settings — `timeout` (default 120000 ms) and `pollingInterval` (default -5000 ms) — and the task shows `isInvisible` for invisible widgets. Solution: `gRecaptchaResponse`. - -### recaptcha_v2_enterprise.js - -[Source code](recaptcha_v2_enterprise.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#recaptcha-v2-enterprise) - -Same two chains, plus `enterprisePayload`. Enterprise widgets render through -`grecaptcha.enterprise.render()`, and any extra parameters the site passes there must be forwarded -in that object — omit them and the site rejects an otherwise valid token. Solution: -`gRecaptchaResponse`. - - - -A single chain: v3 has no with-proxy variant. `minScore` is required and controls how hard the task -is — `0.3` fastest, `0.7` balanced, `0.9` highest and slowest — so the client is created with -`timeout: 180000`. `pageAction` should mirror the action the site sets in `grecaptcha.execute()`. -Commented-out lines show `isEnterprise` and `apiDomain` for sites loading from `recaptcha.net`. -Solution: `gRecaptchaResponse`. - -### turnstile.js - -[Source code](turnstile.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#cloudflare-turnstile) - -Proxyless and with-proxy chains. The commented optional fields matter more here than elsewhere: -`action`, `data` (`data-cdata`) and `pagedata` (`chlPageData`, lowercase unlike the rest) are required for Cloudflare Challenge -pages. Note the User-Agent rule spelled out in the comments — there's no `userAgent` input field; -the worker picks its own and returns it as `result.userAgent`, and the browser or bot submitting -the token must send that exact one back. Solution: `token` (plus `userAgent`). - -### geetest_v3.js - -[Source code](geetest_v3.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#geetest-v3) - -Structurally the odd one out. `challenge` is session-specific and must be fresh per task, so the -file opens with a `fetch(...).then(response => response.json())` chain and **nests both solve blocks -inside** the `.then(({ challenge }) => { … })` callback — the tasks cannot be built until the value -arrives. This is the clearest place to see what `await` buys you: the async version expresses the -same dependency as three flat statements. - -That URL — `https://target-site.com/path/to/geetest/init` — is a **placeholder**, so the file does -not run end-to-end unmodified. It marks where the fetch belongs in the flow; point it at your real -source of `challenge` first. - -The client uses `timeout: 300000`, GeeTest being among the slowest -types. `version` is omitted since v3 is the default. Solution: `challenge`, `validate`, `seccode`. - -### geetest_v4.js - -[Source code](geetest_v4.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#geetest-v4) - -Same `GeeTestProxyless`/`GeeTest` classes as v3, but v4 identifies the widget differently: no `gt`, -no `challenge`, instead `version: 4` and `captcha_id` inside `initParameters`. No pre-fetch is -needed, so this file keeps the flat two-chain layout. Same extended timeouts. Solution: -`captcha_id`, `lot_number`, `pass_token`, `gen_time`, `captcha_output`. - -### yandex_smartcaptcha.js - -[Source code](yandex_smartcaptcha.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#yandex-smartcaptcha) - -The **token** challenge, proxyless and with proxy. `websiteKey` is the sitekey from the page source -[coordinates.js](coordinates.js) instead — different task class entirely. Solution: `token`. - -### tencent.js - -[Source code](tencent.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#tencent) - -Proxyless and with proxy. `appId` comes from the page source; `captchaScript` is only needed when -the site loads the widget from a non-default script URL. Solution: `appid`, `ret`, `ticket`, -`randstr`. - -### image_to_text.js - -[Source code](image_to_text.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#image-to-text) - -Three chains, no proxy variant, all active. *Basic* (`basicTask`) sends the shipped -`text-captcha.png` sample with the character-set hints that match it — `numeric: 2` (letters), -`minLength`/`maxLength`. *Advanced* (`advancedTask`) solves a different image entirely, -`captcha-math.png`, using the rest of the hints — `phrase`, `math`, `comment`, and -`captcha-math-instructions.png` passed as `imgInstructions`. *With language pool* -(`languagePoolTask`) demonstrates the one API detail that is easy to get wrong: `languagePool` is -the **second argument to `solve()`**, not a task field — -`captchaSolver.solve(languagePoolTask, 'en')`, accepting `'en'` or `'ru'`. - -Reads its images via `new URL(..., import.meta.url)`, so the path holds **regardless of the working -directory** and the script runs as-is. Solution: `text`. - -### coordinates.js - -[Source code](coordinates.js) · [API documentation](https://captcha-solver.com/en/docs/captcha-types#coordinates) - -Three chains, no proxy variant, all active. *Basic* (`basicTask`) submits the shipped grid captcha -(`coordinates-captcha.png`) plus a `comment` telling the worker what to click. *Advanced* -(`advancedTask`) solves a different image, a real traffic-lights photo (`traffic-lights.png`), with -`traffic-lights-instructions.png` as `imgInstructions` and `minClicks`/`maxClicks` limits. *Yandex -SmartCaptcha image mode* (`yandexTask`) reuses the same `CoordinatesTask` with -click-order hint is baked into the image itself, so no separate `imgInstructions` is passed — this is -how the image variant of Yandex SmartCaptcha is solved. - -Reads its images via -`new URL(..., import.meta.url)`, so the path holds **regardless of the working directory** and the -script runs as-is. Solution: `coordinates`, an array of `{ x, y }` points. diff --git a/examples/sync/coordinates.js b/examples/sync/coordinates.js deleted file mode 100644 index 8d7180d..0000000 --- a/examples/sync/coordinates.js +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Example: Solve a click-based image captcha using CoordinatesTask. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. That is all: the sample - * captcha ships with the repository, in examples/assets/. - * Point the read below at your own file to solve a different image, and use - * comment to tell the worker what to click on it. - * No proxy is required. The image is submitted directly to the service. - */ - -import 'dotenv/config'; -import fs from 'fs'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// Read and encode the captcha image to base64. -// The body must be a pure base64 string without the data:image/...;base64, prefix. -// The path is resolved against this file rather than the working directory, so -// the example runs from anywhere -- including from the repository root. -const body = fs.readFileSync(new URL('../assets/coordinates-captcha.png', import.meta.url)).toString('base64'); - -// --- Basic example --- -// Solves a simple click-based captcha with a hint for the worker. The comment -// repeats the instruction printed on the sample image, because nothing -// guarantees the worker reads the text baked into the picture. -const basicTask = new Tasks.CoordinatesTask({ - body: body, // Base64-encoded captcha image (required) - comment: 'click on all squares with street signs' // Text hint for the worker -}); - -captchaSolver.solve(basicTask) - .then((result) => { - // Solution contains { coordinates: [{ x: 358, y: 268 }] } - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); - -// --- Advanced example --- -// Solves a captcha (examples/assets/traffic-lights.png) with instruction image -// (examples/assets/traffic-lights-instructions.png) and click count limits. -// Uses its own body -- the basic example's sample image above isn't traffic lights. -const trafficBody = fs.readFileSync(new URL('../assets/traffic-lights.png', import.meta.url)).toString('base64'); -const imgInstructions = fs.readFileSync( - new URL('../assets/traffic-lights-instructions.png', import.meta.url) -).toString('base64'); - -const advancedTask = new Tasks.CoordinatesTask({ - body: trafficBody, // Base64-encoded captcha image - comment: 'click on all traffic lights', // Text hint for the worker - imgInstructions: imgInstructions, // Optional instruction image - minClicks: 1, // Minimum number of clicks (default 1) - maxClicks: 3 // Maximum number of clicks allowed -}); - -captchaSolver.solve(advancedTask) - .then((result) => { - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); diff --git a/examples/sync/geetest_v3.js b/examples/sync/geetest_v3.js deleted file mode 100644 index 22cb60b..0000000 --- a/examples/sync/geetest_v3.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Example: Solve a GeeTest v3 challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL, gt, and challenge with values from your target page. - * Important: the challenge value is dynamic. Fetch a fresh one for each request. - * - * NOTE: "https://target-site.com/path/to/geetest/init" below is a PLACEHOLDER, not a - * real endpoint -- this script will not run end-to-end as-is. Replace it with a request - * to your actual target page (or wherever it exposes a fresh `challenge` value) before - * running this example. It's here only to illustrate where that fetch belongs in the flow. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -// GeeTest tasks may take longer. Increase timeout if needed. -const captchaSolver = new CaptchaClient({ clientKey: apiKey, timeout: 300000 }); - -// Fetch a fresh challenge value from the target page. -// In production, extract this from the page's initGeetest call or network requests. -// "target-site.com" is a placeholder -- point this at your real target before running. -fetch('https://target-site.com/path/to/geetest/init') - .then((response) => response.json()) - .then(({ challenge }) => { - // --- Proxyless example --- - // v3 is the default version, so the version field can be omitted. - const proxylessTask = new Tasks.GeeTestProxyless({ - websiteURL: 'https://example.com/login', // Full URL of the page with GeeTest - gt: 'f2ae6cadcf7886856696c46d84d109d1', // Public key of the GeeTest widget - challenge: challenge // Session-specific value, must be fresh - // Optional fields - // geetestApiServerSubdomain: 'api-na.geetest.com', // Custom API subdomain - // initParameters: {...}, // Extra params from initGeetest call - // risk_type: 'slide', // Dynamic value from the page's captcha-loading request, if present (note: snake_case, not riskType) - // userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...', - }); - - captchaSolver.solve(proxylessTask) - .then((result) => { - // Solution contains { challenge, validate, seccode } - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); - - // --- With proxy example --- - const proxyTask = new Tasks.GeeTest({ - websiteURL: 'https://example.com/login', - gt: 'f2ae6cadcf7886856696c46d84d109d1', - challenge: challenge, - proxyType: 'http', // http, socks4, or socks5 - proxyAddress: '1.2.3.4', // Proxy IP address - proxyPort: 8080, // Proxy port - proxyLogin: 'user', // Login for proxy authorization (optional) - proxyPassword: 'password' // Password for proxy authorization (optional) - }); - - captchaSolver.solve(proxyTask) - .then((result) => { - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); - }) - .catch((error) => { - console.error(error); - }); diff --git a/examples/sync/geetest_v4.js b/examples/sync/geetest_v4.js deleted file mode 100644 index 4915967..0000000 --- a/examples/sync/geetest_v4.js +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Example: Solve a GeeTest v4 challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL and captcha_id with values from your target page. - * GeeTest v4 drops gt/challenge entirely. It uses captcha_id instead. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -// GeeTest v4 tasks may take longer. Increase timeout if needed. -const captchaSolver = new CaptchaClient({ clientKey: apiKey, timeout: 300000 }); - -// --- Proxyless example --- -// v4 drops gt/challenge. The widget is identified by captcha_id inside initParameters. -const proxylessTask = new Tasks.GeeTestProxyless({ - websiteURL: 'https://example.com/login', // Full URL of the page with GeeTest v4 - version: 4, // Required: must be 4 for this version - initParameters: { // Required: must contain captcha_id - captcha_id: 'e392e1d7fd421dc63325744d5a2b9c73' // Static site identifier - } - // Optional fields - // risk_type: 'slide', // Dynamic value from the page's captcha-loading request, if present (note: snake_case, not riskType) - // userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...', -}); - -captchaSolver.solve(proxylessTask) - .then((result) => { - // Solution contains { captcha_id, lot_number, pass_token, gen_time, captcha_output } - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); - -// --- With proxy example --- -const proxyTask = new Tasks.GeeTest({ - websiteURL: 'https://example.com/login', - version: 4, - initParameters: { - captcha_id: 'e392e1d7fd421dc63325744d5a2b9c73' - }, - proxyType: 'http', // http, socks4, or socks5 - proxyAddress: '1.2.3.4', // Proxy IP address - proxyPort: 8080, // Proxy port - proxyLogin: 'user', // Login for proxy authorization (optional) - proxyPassword: 'password' // Password for proxy authorization (optional) -}); - -captchaSolver.solve(proxyTask) - .then((result) => { - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); diff --git a/examples/sync/image_to_text.js b/examples/sync/image_to_text.js deleted file mode 100644 index fc638c6..0000000 --- a/examples/sync/image_to_text.js +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Example: Solve an Image to Text challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. That is all: the sample - * captcha ships with the repository, in examples/assets/. - * Point the read below at your own file to solve a different image. - * Use optional fields to give hints to the worker for faster solving. - */ - -import 'dotenv/config'; -import fs from 'fs'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -// Image to Text tasks are usually fast. Default timeout is fine. -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// Read and encode the captcha image to base64. -// The body must be a pure base64 string without the data:image/...;base64, prefix. -// The path is resolved against this file rather than the working directory, so -// the example runs from anywhere -- including from the repository root. -const body = fs.readFileSync(new URL('../assets/text-captcha.png', import.meta.url)).toString('base64'); - -// --- Basic example --- -// Solves a simple image captcha with character set hints. The hints below match -// the sample image, which is letters and no digits -- adjust them for your own. -const basicTask = new Tasks.ImageToText({ - body: body, // Base64-encoded image (required) - numeric: 2, // 2 = letters only - minLength: 4, // Minimum expected answer length - maxLength: 6 // Maximum expected answer length -}); - -captchaSolver.solve(basicTask) - .then((result) => { - // Solution contains { text: "aB3fX9" } - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); - -// --- Advanced example --- -// Solves a math captcha (examples/assets/captcha-math.png) with comment and -// instruction image (examples/assets/captcha-math-instructions.png). Uses its -// own body -- the basic example's sample image above isn't a math captcha. -const mathBody = fs.readFileSync(new URL('../assets/captcha-math.png', import.meta.url)).toString('base64'); -const imgInstructions = fs.readFileSync( - new URL('../assets/captcha-math-instructions.png', import.meta.url) -).toString('base64'); - -const advancedTask = new Tasks.ImageToText({ - body: mathBody, // Base64-encoded captcha image - // Optional fields (pass only if needed by the captcha type) - phrase: false, // true if answer has multiple words - case_: true, // true if answer is case-sensitive - numeric: 0, // 0 = not specified, 1 = digits, 2 = letters, 3 = any with digits, 4 = any with letters - math: true, // true if image is a math expression to solve - minLength: 1, // Minimum answer length - maxLength: 10, // Maximum answer length - comment: 'Enter the result of the equation', // Text hint for the worker - imgInstructions: imgInstructions // Optional instruction image for the worker -}); - -captchaSolver.solve(advancedTask) - .then((result) => { - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); - -// --- With language pool --- -// The languagePool parameter selects the worker pool by language. -// Pass it as the second argument to solve(), not inside the task. -// Accepted values: "en" (English) or "ru" (Russian). -const languagePoolTask = new Tasks.ImageToText({ - body: body, - numeric: 2, - minLength: 4, - maxLength: 6 -}); - -captchaSolver.solve(languagePoolTask, 'en') // Picks English-speaking worker pool - .then((result) => { - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); diff --git a/examples/sync/recaptcha_v2.js b/examples/sync/recaptcha_v2.js deleted file mode 100644 index 1de91df..0000000 --- a/examples/sync/recaptcha_v2.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Example: Solve a reCAPTCHA v2 challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL and websiteKey with values from your target page. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -// Create a solver instance with your API key. -// Optional: timeout (ms to wait for solution, default 120000) -// Optional: pollingInterval (ms between status checks, default 5000) -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// --- Proxyless example --- -// Solves reCAPTCHA v2 without a proxy. The service uses its own IP addresses. -const proxylessTask = new Tasks.RecaptchaV2Proxyless({ - websiteURL: 'https://example.com/login', // Full URL of the page with captcha - websiteKey: '6Le-xxxxxxxxxxxxxxxxxxxxxxxxxxxx', // data-sitekey attribute value - isInvisible: false // Set true for invisible reCAPTCHA -}); - -captchaSolver.solve(proxylessTask) - .then((result) => { - // Solution contains { gRecaptchaResponse: "03AGdBq..." } - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); - -// --- With proxy example --- -// Solves reCAPTCHA v2 through your own proxy. Required when the target site is -// geo-restricted or you need session consistency. -const proxyTask = new Tasks.RecaptchaV2({ - websiteURL: 'https://example.com/login', - websiteKey: '6Le-xxxxxxxxxxxxxxxxxxxxxxxxxxxx', - // --- Proxy parameters --- - proxyType: 'http', // http, socks4, or socks5 - proxyAddress: '1.2.3.4', // Proxy IP address - proxyPort: 8080, // Proxy port - proxyLogin: 'user', // Login for proxy authorization (optional) - proxyPassword: 'password' // Password for proxy authorization (optional) -}); - -captchaSolver.solve(proxyTask) - .then((result) => { - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); diff --git a/examples/sync/recaptcha_v2_enterprise.js b/examples/sync/recaptcha_v2_enterprise.js deleted file mode 100644 index 083c82f..0000000 --- a/examples/sync/recaptcha_v2_enterprise.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Example: Solve a reCAPTCHA v2 Enterprise challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL and websiteKey with values from your target page. - * If the site uses enterprisePayload, extract and pass it or the token may be rejected. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// --- Proxyless example --- -// Enterprise captchas are loaded via the reCAPTCHA Enterprise API. If the site -// passes extra parameters to grecaptcha.enterprise.render(), pass them as -// enterprisePayload or the token will be rejected. -const proxylessTask = new Tasks.RecaptchaV2EnterpriseProxyless({ - websiteURL: 'https://example.com/login', - websiteKey: '6Le-xxxxxxxxxxxxxxxxxxxxxxxxxxxx', - isInvisible: false - // Optional fields (pass only if the target site requires them): - // enterprisePayload: { s: 'value-from-page' }, - // apiDomain: 'recaptcha.net', - // userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...', - // cookies: 'session=abc123; token=xyz789', -}); - -captchaSolver.solve(proxylessTask) - .then((result) => { - // Solution contains { gRecaptchaResponse: "03AGdBq..." } - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); - -// --- With proxy example --- -const proxyTask = new Tasks.RecaptchaV2Enterprise({ - websiteURL: 'https://example.com/login', - websiteKey: '6Le-xxxxxxxxxxxxxxxxxxxxxxxxxxxx', - proxyType: 'http', - proxyAddress: '1.2.3.4', - proxyPort: 8080, - proxyLogin: 'user', - proxyPassword: 'password', - isInvisible: false -}); - -captchaSolver.solve(proxyTask) - .then((result) => { - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); diff --git a/examples/sync/tencent.js b/examples/sync/tencent.js deleted file mode 100644 index c56ec98..0000000 --- a/examples/sync/tencent.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Example: Solve a Tencent captcha challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL and appId with values from your target page. - * Pass captchaScript if the site uses a non-default script URL. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// --- Proxyless example --- -// The service's own proxies are used to solve the captcha. -// appId is found in the page source code. captchaScript is optional if the site uses the default. -const proxylessTask = new Tasks.TencentTaskProxyless({ - websiteURL: 'https://example.com/login', // Full URL of the page with captcha - appId: '190014885' // appId from page source code (required) - // Optional fields: - // captchaScript: 'https://turing.captcha.qcloud.com/TCaptcha.js', -}); - -captchaSolver.solve(proxylessTask) - .then((result) => { - // Solution contains { appid, ret, ticket, randstr } - // Pass all four values together into the page's captcha callback as-is. - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); - -// --- With proxy example --- -// Use when the target site is geo-restricted or you need a consistent session. -const proxyTask = new Tasks.TencentTask({ - websiteURL: 'https://example.com/login', - appId: '190014885', - // --- Proxy parameters --- - proxyType: 'http', // http, socks4, or socks5 - proxyAddress: '1.2.3.4', // Proxy IP address - proxyPort: 8080, // Proxy port - proxyLogin: 'user', // Login for proxy authorization (optional) - proxyPassword: 'password' // Password for proxy authorization (optional) -}); - -captchaSolver.solve(proxyTask) - .then((result) => { - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); diff --git a/examples/sync/turnstile.js b/examples/sync/turnstile.js deleted file mode 100644 index 8eaea0b..0000000 --- a/examples/sync/turnstile.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Example: Solve a Cloudflare Turnstile challenge. - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL and websiteKey with values from your target page. - * For Cloudflare Challenge pages, also extract and pass action, data, pagedata, - * and the current browser userAgent. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// --- Proxyless example --- -// For a Cloudflare Challenge page, pass the current browser User-Agent together -// with action, data, and pagedata. Do not change the browser User-Agent after solving. -const proxylessTask = new Tasks.TurnstileProxyless({ - websiteURL: 'https://example.com/login', // Full URL of the page with Turnstile - websiteKey: '0x4AAAAAAAxxxxxxxxxxxxxxxx', // data-sitekey attribute value - // Optional fields (pass only if the target site sets them) - // action: 'login', // Value of data-action attribute - // data: 'custom-cdata-value', // Value of data-cdata attribute - // pagedata: 'chl-page-data-value', // Value of chlPageData parameter - // userAgent: 'Mozilla/5.0 ...', // Current browser User-Agent -}); - -captchaSolver.solve(proxylessTask) - .then((result) => { - // Solution contains { token: "0.zxcv...", userAgent: "Mozilla/5.0 ..." } - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); - -// --- With proxy example --- -const proxyTask = new Tasks.Turnstile({ - websiteURL: 'https://example.com/login', - websiteKey: '0x4AAAAAAAxxxxxxxxxxxxxxxx', - // --- Proxy parameters --- - proxyType: 'http', // http, socks4, or socks5 - proxyAddress: '1.2.3.4', // Proxy IP address - proxyPort: 8080, // Proxy port - proxyLogin: 'user', // Login for proxy authorization (optional) - proxyPassword: 'password' // Password for proxy authorization (optional) - // --- Optional fields --- - // action: 'login', - // data: 'custom-cdata-value', - // pagedata: 'chl-page-data-value', -}); - -captchaSolver.solve(proxyTask) - .then((result) => { - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); diff --git a/examples/sync/yandex_smartcaptcha.js b/examples/sync/yandex_smartcaptcha.js deleted file mode 100644 index dfc0c52..0000000 --- a/examples/sync/yandex_smartcaptcha.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Example: Solve a Yandex SmartCaptcha challenge (token-based). - * - * Prerequisites: - * Set the CAPTCHA_API_KEY environment variable. - * Replace websiteURL and websiteKey with values from your target page. - */ - -import 'dotenv/config'; -import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; - -const apiKey = process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY'; - -const captchaSolver = new CaptchaClient({ clientKey: apiKey }); - -// --- Proxyless example --- -// The service's own proxies are used to solve the captcha. -// websiteKey is the sitekey value from the page code or captcha iframe. -const proxylessTask = new Tasks.YandexSmartCaptchaTaskProxyless({ - websiteURL: 'https://example.com/login', // Full URL of the page with captcha - websiteKey: 'FEXfAbHQsToo97VidNVk3j4dC74nGW1DgdxK4OoR' // sitekey from page code or iframe - // Optional fields: - // userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...', - // cookies: 'session=abc123; token=xyz789', -}); - -captchaSolver.solve(proxylessTask) - .then((result) => { - // Solution contains { token: "dV9xNjYyNTU3NjkxO4k9OTQuNVMuMjkuMjM9..." } - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); - -// --- With proxy example --- -// Note: this is the only captcha type where an https proxy is accepted. -const proxyTask = new Tasks.YandexSmartCaptchaTask({ - websiteURL: 'https://example.com/login', - websiteKey: 'FEXfAbHQsToo97VidNVk3j4dC74nGW1DgdxK4OoR', - // --- Proxy parameters --- - proxyType: 'http', // http, https, socks4, or socks5 (https is accepted only for this type) - proxyAddress: '1.2.3.4', // Proxy IP address - proxyPort: 8080, // Proxy port - proxyLogin: 'user', // Login for proxy authorization (optional) - proxyPassword: 'password' // Password for proxy authorization (optional) -}); - -captchaSolver.solve(proxyTask) - .then((result) => { - console.log('result:', result); - }) - .catch((error) => { - console.error(error); - }); diff --git a/examples/tencent.js b/examples/tencent.js new file mode 100644 index 0000000..e827ff4 --- /dev/null +++ b/examples/tencent.js @@ -0,0 +1,12 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.TencentTaskProxyless({ + websiteURL: 'https://example.com/register', + appId: 'YOUR_APP_ID' +}); + +client.solve(task) + .then((solution) => console.log(solution)) + .catch(console.error); diff --git a/examples/tencent_proxy.js b/examples/tencent_proxy.js new file mode 100644 index 0000000..b92f239 --- /dev/null +++ b/examples/tencent_proxy.js @@ -0,0 +1,15 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.TencentTask({ + websiteURL: 'https://example.com/register', + appId: 'YOUR_APP_ID', + proxyType: 'http', + proxyAddress: '1.2.3.4', + proxyPort: 8080 +}); + +client.solve(task) + .then((solution) => console.log(solution)) + .catch(console.error); diff --git a/examples/turnstile.js b/examples/turnstile.js new file mode 100644 index 0000000..ad66730 --- /dev/null +++ b/examples/turnstile.js @@ -0,0 +1,12 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.TurnstileProxyless({ + websiteURL: 'https://example.com/login', + websiteKey: 'YOUR_WEBSITE_KEY' +}); + +client.solve(task) + .then((solution) => console.log(solution.token)) + .catch(console.error); diff --git a/examples/turnstile_proxy.js b/examples/turnstile_proxy.js new file mode 100644 index 0000000..31fb852 --- /dev/null +++ b/examples/turnstile_proxy.js @@ -0,0 +1,15 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.Turnstile({ + websiteURL: 'https://example.com/login', + websiteKey: 'YOUR_WEBSITE_KEY', + proxyType: 'http', + proxyAddress: '1.2.3.4', + proxyPort: 8080 +}); + +client.solve(task) + .then((solution) => console.log(solution.token)) + .catch(console.error); diff --git a/examples/yandex_smartcaptcha.js b/examples/yandex_smartcaptcha.js new file mode 100644 index 0000000..3397175 --- /dev/null +++ b/examples/yandex_smartcaptcha.js @@ -0,0 +1,12 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.YandexSmartCaptchaTaskProxyless({ + websiteURL: 'https://example.com/login', + websiteKey: 'YOUR_WEBSITE_KEY' +}); + +client.solve(task) + .then((solution) => console.log(solution.token)) + .catch(console.error); diff --git a/examples/yandex_smartcaptcha_proxy.js b/examples/yandex_smartcaptcha_proxy.js new file mode 100644 index 0000000..c3250d6 --- /dev/null +++ b/examples/yandex_smartcaptcha_proxy.js @@ -0,0 +1,15 @@ +import 'dotenv/config'; +import { CaptchaClient, Tasks } from '@captcha-solver-api/javascript-sdk'; + +const client = new CaptchaClient({ clientKey: process.env.CAPTCHA_API_KEY || 'YOUR_API_KEY' }); +const task = new Tasks.YandexSmartCaptchaTask({ + websiteURL: 'https://example.com/login', + websiteKey: 'YOUR_WEBSITE_KEY', + proxyType: 'http', + proxyAddress: '1.2.3.4', + proxyPort: 8080 +}); + +client.solve(task) + .then((solution) => console.log(solution.token)) + .catch(console.error); From c8d93b6e1ae5155663309e7eee2d228904c6e1c2 Mon Sep 17 00:00:00 2001 From: dariyaanis Date: Thu, 29 Oct 2026 12:37:09 +0300 Subject: [PATCH 11/16] docs: add JSDoc usage examples --- src/client.ts | 16 ++++++++++++++++ src/tasks.ts | 15 +++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/client.ts b/src/client.ts index f477809..428b383 100644 --- a/src/client.ts +++ b/src/client.ts @@ -120,6 +120,10 @@ export class CaptchaClient { * @throws {ApiError} If the API rejected the task. * @throws {TimeoutError} If the request exceeded {@link timeout}. * @throws {NetworkError} On a transport failure. + * @example + * ```ts + * const taskId = await client.createTask(task); + * ``` */ async createTask(task: BaseTask, languagePool: string | null = null): Promise { const payload: Record = { clientKey: this.clientKey, task: task.toDict() }; if (languagePool !== null) payload.languagePool = languagePool; @@ -137,6 +141,10 @@ export class CaptchaClient { * @throws {ApiError} If the task failed on the API side. * @throws {TimeoutError} If the request exceeded {@link timeout}. * @throws {NetworkError} On a transport failure. + * @example + * ```ts + * const result = await client.getTaskResult(taskId); + * ``` */ getTaskResult(taskId: number): Promise { return this._request('/getTaskResult', { clientKey: this.clientKey, taskId }); } @@ -147,6 +155,10 @@ export class CaptchaClient { * @throws {ApiError} If the API key is invalid. * @throws {TimeoutError} If the request exceeded {@link timeout}. * @throws {NetworkError} On a transport failure. + * @example + * ```ts + * const balance = await client.getBalance(); + * ``` */ async getBalance(): Promise { return Number((await this._request('/getBalance', { clientKey: this.clientKey })).balance); } @@ -164,6 +176,10 @@ export class CaptchaClient { * @throws {TimeoutError} If no solution arrived within {@link timeout}. * @throws {ApiError} If the API rejected the task or the task failed while solving. * @throws {NetworkError} On a transport failure. + * @example + * ```ts + * client.solve(task).then(console.log).catch(console.error); + * ``` */ async solve>(task: T, languagePool: string | null = null): Promise> { const taskId = await this.createTask(task, languagePool); const startedAt = Date.now(); diff --git a/src/tasks.ts b/src/tasks.ts index 99b632f..d56c715 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -261,6 +261,7 @@ export interface RecaptchaV2Params extends RecaptchaV2ProxylessParams, ProxyPara /** * reCAPTCHA v2 without a proxy (`RecaptchaV2TaskProxyless`). * Solution: {@link RecaptchaSolution}. + * @example `new Tasks.RecaptchaV2Proxyless({ websiteURL, websiteKey })` * * @see https://captcha-solver.com/en/docs/captcha-types#recaptcha-v2 */ @@ -273,6 +274,7 @@ export class RecaptchaV2Proxyless extends BaseTask { /** * reCAPTCHA v2 solved through your own proxy (`RecaptchaV2Task`). * Solution: {@link RecaptchaSolution}. + * @example `new Tasks.RecaptchaV2({ websiteURL, websiteKey, proxyType, proxyAddress, proxyPort })` * * @see https://captcha-solver.com/en/docs/captcha-types#recaptcha-v2 */ @@ -313,6 +315,7 @@ export interface RecaptchaV2EnterpriseParams extends RecaptchaV2EnterpriseProxyl /** * reCAPTCHA v2 Enterprise without a proxy (`RecaptchaV2EnterpriseTaskProxyless`). * Solution: {@link RecaptchaSolution}. + * @example `new Tasks.RecaptchaV2EnterpriseProxyless({ websiteURL, websiteKey })` * * @see https://captcha-solver.com/en/docs/captcha-types#recaptcha-v2-enterprise */ @@ -325,6 +328,7 @@ export class RecaptchaV2EnterpriseProxyless extends BaseTask /** * reCAPTCHA v2 Enterprise solved through your own proxy (`RecaptchaV2EnterpriseTask`). * Solution: {@link RecaptchaSolution}. + * @example `new Tasks.RecaptchaV2Enterprise({ websiteURL, websiteKey, proxyType, proxyAddress, proxyPort })` * * @see https://captcha-solver.com/en/docs/captcha-types#recaptcha-v2-enterprise */ @@ -358,6 +362,7 @@ export interface RecaptchaV3ProxylessParams { * Score-based reCAPTCHA v3 (`RecaptchaV3TaskProxyless`). * The API supports this type only without a customer proxy. * Solution: {@link RecaptchaSolution}. + * @example `new Tasks.RecaptchaV3Proxyless({ websiteURL, websiteKey, minScore: 0.3 })` * * @see https://captcha-solver.com/en/docs/captcha-types#recaptcha-v3 */ @@ -400,6 +405,7 @@ export interface TurnstileParams extends TurnstileProxylessParams, ProxyParams { /** * Cloudflare Turnstile without a proxy (`TurnstileTaskProxyless`). * Solution: {@link TurnstileSolution}. + * @example `new Tasks.TurnstileProxyless({ websiteURL, websiteKey })` * * @see https://captcha-solver.com/en/docs/captcha-types#cloudflare-turnstile */ @@ -412,6 +418,7 @@ export class TurnstileProxyless extends BaseTask { /** * Cloudflare Turnstile solved through your own proxy (`TurnstileTask`). * Solution: {@link TurnstileSolution}. + * @example `new Tasks.Turnstile({ websiteURL, websiteKey, proxyType, proxyAddress, proxyPort })` * * @see https://captcha-solver.com/en/docs/captcha-types#cloudflare-turnstile */ @@ -461,6 +468,7 @@ export interface GeeTestParams extends GeeTestProxylessParams, ProxyParams {} * GeeTest v3 / v4 without a proxy (`GeeTestTaskProxyless`). * Pass `gt` + `challenge` for v3, or `version: 4` + `initParameters.captcha_id` for v4. * Solution: {@link GeeTestSolution}. + * @example `new Tasks.GeeTestProxyless({ websiteURL, gt, challenge })` * * @see https://captcha-solver.com/en/docs/captcha-types#geetest-v3 * @see https://captcha-solver.com/en/docs/captcha-types#geetest-v4 @@ -474,6 +482,7 @@ export class GeeTestProxyless extends BaseTask { /** * GeeTest v3 / v4 solved through your own proxy (`GeeTestTask`). * Solution: {@link GeeTestSolution}. + * @example `new Tasks.GeeTest({ websiteURL, gt, challenge, proxyType, proxyAddress, proxyPort })` * * @see https://captcha-solver.com/en/docs/captcha-types#geetest-v3 * @see https://captcha-solver.com/en/docs/captcha-types#geetest-v4 @@ -524,6 +533,7 @@ export interface ImageToTextParams { * Text / number / math recognition in an image captcha (`ImageToTextTask`). * The image is submitted directly, so there is no proxy variant. * Solution: {@link ImageToTextSolution}. + * @example `new Tasks.ImageToText({ body: imageBase64 })` * * @see https://captcha-solver.com/en/docs/captcha-types#image-to-text */ @@ -559,6 +569,7 @@ export interface YandexSmartCaptchaTaskParams extends YandexSmartCaptchaTaskProx * Token-based Yandex SmartCaptcha without a proxy (`YandexSmartCaptchaTaskProxyless`). * For the image challenge use {@link CoordinatesTask} instead. * Solution: {@link YandexSmartCaptchaSolution}. + * @example `new Tasks.YandexSmartCaptchaTaskProxyless({ websiteURL, websiteKey })` * * @see https://captcha-solver.com/en/docs/captcha-types#yandex-smartcaptcha */ @@ -571,6 +582,7 @@ export class YandexSmartCaptchaTaskProxyless extends BaseTask { /** * Tencent Captcha solved through your own proxy (`TencentTask`). * Solution: {@link TencentSolution}. + * @example `new Tasks.TencentTask({ websiteURL, appId, proxyType, proxyAddress, proxyPort })` * * @see https://captcha-solver.com/en/docs/captcha-types#tencent */ From ed9b9a793aaedd8f75355ec3ad1bce876be5e561 Mon Sep 17 00:00:00 2001 From: dariyaanis Date: Thu, 29 Oct 2026 12:37:40 +0300 Subject: [PATCH 12/16] docs: clarify critical captcha integration notes --- README.md | 7 +++++++ src/tasks.ts | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 206a43b..838e605 100644 --- a/README.md +++ b/README.md @@ -276,6 +276,10 @@ as reCAPTCHA v2, plus: | `enterprisePayload` | no | Extra parameters passed to `grecaptcha.enterprise.render` on the page, e.g. `{ s: '...' }`. | | `apiDomain` | no | Defaults to `google.com`. | +> **Important:** If the page passes values such as `s` to +> `grecaptcha.enterprise.render`, include them in `enterprisePayload`. A token +> created without the page's payload can be rejected by the target website. + **Response fields** - `gRecaptchaResponse` — submit this token to the Enterprise reCAPTCHA callback. @@ -394,6 +398,9 @@ browser session involved. | `comment` | no | Free-text hint for the worker. | | `imgInstructions` | no | Optional supplementary instruction image, base64-encoded. | +> **Important:** Pass only the raw base64 data in `body` and +> `imgInstructions`. Remove prefixes such as `data:image/png;base64,`. + **Response fields** - `text` — the recognized text or calculated answer. diff --git a/src/tasks.ts b/src/tasks.ts index d56c715..f24b720 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -86,8 +86,9 @@ export interface TurnstileSolution { /** Token to submit as `cf-turnstile-response`. */ token: string; /** - * User-Agent the worker actually solved with. The token is tied to it, - * so submit the token with this exact User-Agent. + * User-Agent reported by the solving service, when present. For Cloudflare + * Challenge pages, keep using the browser User-Agent sent in the task; do + * not replace it with this returned value. */ userAgent: string; [key: string]: unknown; From 767044c6fe29662cf328773f6978abb531b8e714 Mon Sep 17 00:00:00 2001 From: dariyaanis Date: Thu, 29 Oct 2026 12:55:54 +0300 Subject: [PATCH 13/16] docs: use promise chains in all examples --- src/client.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/client.ts b/src/client.ts index 428b383..537af5e 100644 --- a/src/client.ts +++ b/src/client.ts @@ -58,7 +58,9 @@ export type SolutionOf = T extends BaseTask ? S : Record console.log(gRecaptchaResponse)) + * .catch(console.error); * ``` */ export class CaptchaClient { @@ -122,7 +124,9 @@ export class CaptchaClient { * @throws {NetworkError} On a transport failure. * @example * ```ts - * const taskId = await client.createTask(task); + * client.createTask(task) + * .then((taskId) => console.log(taskId)) + * .catch(console.error); * ``` */ async createTask(task: BaseTask, languagePool: string | null = null): Promise { @@ -143,7 +147,9 @@ export class CaptchaClient { * @throws {NetworkError} On a transport failure. * @example * ```ts - * const result = await client.getTaskResult(taskId); + * client.getTaskResult(taskId) + * .then(console.log) + * .catch(console.error); * ``` */ getTaskResult(taskId: number): Promise { return this._request('/getTaskResult', { clientKey: this.clientKey, taskId }); } @@ -157,7 +163,9 @@ export class CaptchaClient { * @throws {NetworkError} On a transport failure. * @example * ```ts - * const balance = await client.getBalance(); + * client.getBalance() + * .then((balance) => console.log(balance)) + * .catch(console.error); * ``` */ async getBalance(): Promise { return Number((await this._request('/getBalance', { clientKey: this.clientKey })).balance); } From 15f5473b408fe7b787c97a14029a01b9c7844d85 Mon Sep 17 00:00:00 2001 From: dzmitry-duboyski Date: Wed, 4 Nov 2026 00:26:25 +0300 Subject: [PATCH 14/16] fix: validate Yandex SmartCaptcha image task parameters Co-Authored-By: Claude Opus 5.5 --- src/tasks.ts | 5 +++-- tests/unit/task-validation.test.js | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tasks.ts b/src/tasks.ts index dcf7d4b..0740c7b 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -14,6 +14,7 @@ const REQUIRED_TASK_PARAMS: Record = { YandexSmartCaptchaTaskProxyless: ['websiteURL', 'websiteKey'], YandexSmartCaptchaTask: ['websiteURL', 'websiteKey', 'proxyType', 'proxyAddress', 'proxyPort'], CoordinatesTask: ['body'], + SmartCaptchaTask: ['image', 'imgInstructions'], TencentTaskProxyless: ['websiteURL', 'appId'], TencentTask: ['websiteURL', 'appId', 'proxyType', 'proxyAddress', 'proxyPort'] }; @@ -568,7 +569,7 @@ export interface YandexSmartCaptchaTaskParams extends YandexSmartCaptchaTaskProx /** * Token-based Yandex SmartCaptcha without a proxy (`YandexSmartCaptchaTaskProxyless`). - * For the image challenge use {@link CoordinatesTask} instead. + * For the image challenge use {@link YandexSmartCaptchaImageTask} instead. * Solution: {@link YandexSmartCaptchaSolution}. * @example `new Tasks.YandexSmartCaptchaTaskProxyless({ websiteURL, websiteKey })` * @@ -648,7 +649,7 @@ export interface YandexSmartCaptchaImageTaskParams { * @see https://captcha-solver.com/en/docs/captcha-types#yandex-smartcaptcha */ export class YandexSmartCaptchaImageTask extends BaseTask { - constructor({ image, imgInstructions, comment = null }: YandexSmartCaptchaImageTaskParams) { + constructor({ image, imgInstructions, comment = null }: YandexSmartCaptchaImageTaskParams = {} as YandexSmartCaptchaImageTaskParams) { super({ type: 'SmartCaptchaTask', image, imgInstructions, comment }); } } diff --git a/tests/unit/task-validation.test.js b/tests/unit/task-validation.test.js index b152cdc..e7b34cd 100644 --- a/tests/unit/task-validation.test.js +++ b/tests/unit/task-validation.test.js @@ -3,6 +3,9 @@ import { Tasks, ValidationError } from '@captcha-solver-api/javascript-sdk'; describe('built-in task validation', () => { test('missing constructor arguments produce SDK validation errors', () => { expect(() => new Tasks.ImageToText()).toThrow(ValidationError); + expect(() => new Tasks.YandexSmartCaptchaImageTask()).toThrow( + 'SmartCaptchaTask requires: image, imgInstructions' + ); expect(() => new Tasks.RecaptchaV2Proxyless()).toThrow( 'RecaptchaV2TaskProxyless requires: websiteURL, websiteKey' ); @@ -17,6 +20,8 @@ describe('built-in task validation', () => { ['ImageToText', {}, 'body'], ['YandexSmartCaptchaTaskProxyless', { websiteURL: 'https://example.com' }, 'websiteKey'], ['CoordinatesTask', { body: ' ' }, 'body'], + ['YandexSmartCaptchaImageTask', {}, 'image, imgInstructions'], + ['YandexSmartCaptchaImageTask', { image: 'image', imgInstructions: '' }, 'imgInstructions'], ['TencentTaskProxyless', { websiteURL: 'https://example.com' }, 'appId'] ])('%s reports missing required parameters', (className, params, expected) => { expect(() => new Tasks[className](params)).toThrow(ValidationError); From 5e17bbc33bb039af9585513db442f6f9bd7dd3d5 Mon Sep 17 00:00:00 2001 From: dzmitry-duboyski Date: Wed, 4 Nov 2026 00:31:51 +0300 Subject: [PATCH 15/16] chore: fix brace-expansion audit vulnerability Co-Authored-By: Claude Opus 5.5 --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c4a9784..9455673 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2065,9 +2065,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", - "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { From e4e4cc4413a8af7dd704ec1cfcff10792b012f89 Mon Sep 17 00:00:00 2001 From: dzmitry-duboyski Date: Wed, 4 Nov 2026 00:26:25 +0300 Subject: [PATCH 16/16] chore: set version to 1.1.0 Co-Authored-By: Claude Opus 5.5 --- package-lock.json | 4 ++-- package.json | 5 +++-- src/version.ts | 2 +- tests/unit/async/client.test.js | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9455673..6f3057f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@captcha-solver-api/javascript-sdk", - "version": "1.0.1", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@captcha-solver-api/javascript-sdk", - "version": "1.0.1", + "version": "1.1.0", "license": "MIT", "devDependencies": { "dotenv": "^16.4.0", diff --git a/package.json b/package.json index 3f06ed3..aca194e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@captcha-solver-api/javascript-sdk", - "version": "1.0.1", - "description": "Official JavaScript SDK for the Captcha Solver API. Solve reCAPTCHA v2, Cloudflare Turnstile, GeeTest, Tencent, and image captchas.", + "version": "1.1.0", + "description": "Official JavaScript SDK for the Captcha Solver API. Solve reCAPTCHA v2, Cloudflare Turnstile, GeeTest, Yandex SmartCaptcha, Tencent, and image captchas.", "main": "./dist/index.cjs", "type": "module", "exports": { @@ -38,6 +38,7 @@ "recaptcha-v2", "cloudflare-turnstile", "geetest", + "yandex-smartcaptcha", "tencent-captcha", "image-to-text", "captcha-recognition", diff --git a/src/version.ts b/src/version.ts index 809a5f0..190fd5e 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,2 +1,2 @@ /** SDK version, sent to the API in the `X-SDK` header. Must match `version` in package.json. */ -export const __version__ = '1.0.1'; +export const __version__ = '1.1.0'; diff --git a/tests/unit/async/client.test.js b/tests/unit/async/client.test.js index b9f8cd5..13d717b 100644 --- a/tests/unit/async/client.test.js +++ b/tests/unit/async/client.test.js @@ -36,7 +36,7 @@ describe('CaptchaClient (async)', () => { }); await client.getBalance(); - expect(requestOptions.headers['X-SDK']).toBe('javascript-sdk/1.0.1'); + expect(requestOptions.headers['X-SDK']).toBe('javascript-sdk/1.1.0'); }); test('aborts an HTTP request after timeout', async () => {