captcha-solver-api 1.0.1__tar.gz → 1.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/PKG-INFO +64 -18
  2. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/README.md +59 -17
  3. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/captcha_solver_api/__init__.py +8 -8
  4. captcha_solver_api-1.0.3/captcha_solver_api/_version.py +1 -0
  5. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/captcha_solver_api/async_client.py +11 -6
  6. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/captcha_solver_api/client.py +11 -6
  7. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/captcha_solver_api/exceptions.py +0 -8
  8. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/captcha_solver_api/tasks.py +24 -15
  9. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/captcha_solver_api.egg-info/PKG-INFO +64 -18
  10. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/captcha_solver_api.egg-info/requires.txt +4 -0
  11. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/pyproject.toml +35 -0
  12. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/tests/test_polling.py +29 -27
  13. captcha_solver_api-1.0.1/captcha_solver_api/_version.py +0 -1
  14. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/LICENSE.md +0 -0
  15. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/captcha_solver_api/py.typed +0 -0
  16. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/captcha_solver_api.egg-info/SOURCES.txt +0 -0
  17. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/captcha_solver_api.egg-info/dependency_links.txt +0 -0
  18. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/captcha_solver_api.egg-info/top_level.txt +0 -0
  19. {captcha_solver_api-1.0.1 → captcha_solver_api-1.0.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: captcha-solver-api
3
- Version: 1.0.1
3
+ Version: 1.0.3
4
4
  Summary: Official Python SDK for solving reCAPTCHA v2/v3, Cloudflare Turnstile, GeeTest v3/v4, Yandex SmartCaptcha, Tencent, and image/click captchas via the Captcha Solver API
5
5
  Author: Captcha Solver Team
6
6
  License-Expression: MIT
@@ -32,11 +32,27 @@ Requires-Dist: pytest>=7; extra == "dev"
32
32
  Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
33
33
  Requires-Dist: pytest-mock>=3; extra == "dev"
34
34
  Requires-Dist: python-dotenv>=1.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.6; extra == "dev"
36
+ Requires-Dist: mypy>=1.10; extra == "dev"
37
+ Requires-Dist: types-requests>=2.28; extra == "dev"
38
+ Requires-Dist: pytest-cov>=4; extra == "dev"
35
39
  Dynamic: license-file
36
40
 
37
41
  # Captcha Solver Python SDK
38
42
 
39
- ![python-sdk-banner](https://raw.githubusercontent.com/captcha-solver-api/python-sdk/main/assets/repo-banner-python.png)
43
+ ![python-sdk-banner](https://raw.githubusercontent.com/captcha-solver-api/python-sdk/main/assets/repo-banner-python.png?v=09a4ebb)
44
+
45
+ [![PyPI](https://img.shields.io/pypi/v/captcha-solver-api?logo=pypi&logoColor=white)](https://pypi.org/project/captcha-solver-api/)
46
+ [![Python](https://img.shields.io/pypi/pyversions/captcha-solver-api?logo=python&logoColor=white)](https://pypi.org/project/captcha-solver-api/)
47
+ [![Downloads](https://img.shields.io/pepy/dt/captcha-solver-api)](https://pepy.tech/project/captcha-solver-api)
48
+ [![Tests](https://github.com/captcha-solver-api/python-sdk/actions/workflows/tests.yml/badge.svg)](https://github.com/captcha-solver-api/python-sdk/actions/workflows/tests.yml)
49
+ [![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/dzmitry-duboyski/77b084ed0d46c740183f89ec66af8a95/raw/python-sdk-coverage.json)](https://github.com/captcha-solver-api/python-sdk/actions/workflows/tests.yml)
50
+ [![Typed](https://img.shields.io/badge/typing-typed-blue)](https://github.com/captcha-solver-api/python-sdk/blob/main/captcha_solver_api/py.typed)
51
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/captcha-solver-api/python-sdk/blob/main/LICENSE.md)
52
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
53
+ [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
54
+ [![GitHub Release](https://img.shields.io/github/v/release/captcha-solver-api/python-sdk)](https://github.com/captcha-solver-api/python-sdk/releases)
55
+ [![JavaScript SDK](https://img.shields.io/badge/also_available-JavaScript_SDK-f7df1e?logo=javascript&logoColor=black)](https://github.com/captcha-solver-api/javascript-sdk)
40
56
 
41
57
  Official Python SDK for the Captcha Solver API. Solve reCAPTCHA v2/v3, Cloudflare Turnstile, GeeTest, Yandex SmartCaptcha, Tencent, and image/click captchas with a single method call -- sync or async.
42
58
 
@@ -47,6 +63,7 @@ Full API reference (all endpoints, error codes, captcha-type details): **https:/
47
63
  - [Installation](#installation)
48
64
  - [Configuration](#configuration)
49
65
  - [Quick Start](#quick-start)
66
+ - [Build Faster with AI](#build-faster-with-ai)
50
67
  - [Supported CAPTCHA Types](#supported-captcha-types)
51
68
  - [Client Reference](#client-reference)
52
69
  - [CaptchaClient(...)](#captchaclient)
@@ -74,6 +91,7 @@ Full API reference (all endpoints, error codes, captcha-type details): **https:/
74
91
  - [Running the examples](#running-the-examples)
75
92
  - [Requirements](#requirements)
76
93
  - [API Documentation](#api-documentation)
94
+ - [Useful Links](#useful-links)
77
95
  - [License](#license)
78
96
 
79
97
  ## Installation
@@ -124,6 +142,23 @@ result = client.solve(task)
124
142
  print(result["gRecaptchaResponse"])
125
143
  ```
126
144
 
145
+ ## Build Faster with AI
146
+
147
+ Use our [`llms.txt`](https://captcha-solver.com/llms.txt) as context when asking an
148
+ AI assistant to build or update your integration. It contains the current API
149
+ format, supported task parameters, SDK installation instructions, solution
150
+ fields, and error codes in one file.
151
+
152
+ Copy this prompt and add a description of the page and CAPTCHA you need to
153
+ handle:
154
+
155
+ ```text
156
+ Read https://captcha-solver.com/llms.txt and use the Captcha Solver Python SDK.
157
+ Write a complete integration for this task: [describe your task here].
158
+ Use the documented task parameters and show how to apply the returned solution
159
+ on the target page.
160
+ ```
161
+
127
162
  ## Supported CAPTCHA Types
128
163
 
129
164
  | Type | Proxyless | With Proxy |
@@ -157,7 +192,7 @@ Constructor.
157
192
  | `client_key` | `str` | required | Your Captcha Solver API key. Raises `ValidationError` if empty. |
158
193
  | `base_url` | `str` | `https://api.captcha-solver.com` | API base URL. Override only for self-hosted/staging deployments. |
159
194
  | `timeout` | `int` | `120` | Polling window after task creation, in seconds. Overridable per call; each HTTP request has a separate 30-second timeout. |
160
- | `polling_interval` | `int` | `10` | Seconds before the first `getTaskResult` poll and between subsequent polls, matching the official 2Captcha Python SDK. |
195
+ | `polling_interval` | `int` | `10` | Seconds before the first `getTaskResult` poll and between subsequent polls. |
161
196
  | `language_pool` | `Optional[str]` | `None` | Default worker pool (`"en"` or `"ru"`) applied to every call that doesn't pass its own `language_pool`. |
162
197
 
163
198
  Both clients hold a reusable connection pool (`requests.Session` / `httpx.AsyncClient`)
@@ -353,12 +388,13 @@ that the target page expects in `cf-turnstile-response`.
353
388
  | `action` | no | Value of the widget's `data-action` attribute, if set. |
354
389
  | `data` | no | Custom payload from the widget's `data-cdata` attribute, if set. |
355
390
  | `pagedata` | no | Value of the `chlPageData` parameter, needed for some Cloudflare challenge pages beyond the basic widget. |
356
- | `userAgent` | no | User-Agent to solve with -- the returned token is tied to it, submit with the same one. |
391
+ | `userAgent` | no | Current browser User-Agent. Pass it for Cloudflare Challenge pages; it is not needed for a standalone widget. |
357
392
 
358
393
  **Response:** `token` -- submit as `cf-turnstile-response`. For Cloudflare
359
- Challenge pages, the solution also includes `userAgent`; use that User-Agent
360
- when submitting the token. Pass `action`, `data`, and `pagedata` when the page
361
- provides them. The API field is spelled `pagedata`, all lowercase.
394
+ Challenge pages, pass `action`, `data`, `pagedata`, and the current browser
395
+ `userAgent` in the task. The solution also contains `userAgent`; switch the
396
+ browser or HTTP client to this returned value before invoking the callback with
397
+ the token. The API field is spelled `pagedata`, all lowercase.
362
398
 
363
399
  ```python
364
400
  from captcha_solver_api import CaptchaClient
@@ -366,10 +402,15 @@ from captcha_solver_api.tasks import TurnstileTaskProxyless
366
402
  client = CaptchaClient("your_api_key")
367
403
  task = TurnstileTaskProxyless(
368
404
  websiteURL="https://example.com/login",
369
- websiteKey="YOUR_WEBSITE_KEY"
405
+ websiteKey="YOUR_WEBSITE_KEY",
406
+ action="managed",
407
+ data="INTERCEPTED_CDATA",
408
+ pagedata="INTERCEPTED_CHL_PAGE_DATA",
409
+ userAgent="CURRENT_BROWSER_USER_AGENT",
370
410
  )
371
411
  result = client.solve(task)
372
412
  print(result["token"])
413
+ print(result.get("userAgent"))
373
414
  ```
374
415
 
375
416
  With proxy, use `TurnstileTask` (same proxy fields as reCAPTCHA v2).
@@ -476,7 +517,7 @@ for the widget on the target page. Use the coordinates method for image challeng
476
517
 
477
518
  `YandexSmartCaptchaTaskProxyless` / `YandexSmartCaptchaTask` -- token-based
478
519
  challenge. For the image challenge instead, use `CoordinatesTask` with
479
- `imgType="smart_captcha"` (see [Coordinates](#coordinates-click-captcha) below).
520
+ [Coordinates](#coordinates-click-captcha) is available for generic click captchas.
480
521
 
481
522
  | Parameter | Required | Description |
482
523
  |---|---|---|
@@ -519,10 +560,9 @@ SmartCaptcha's image challenge. No proxy variant -- the image is submitted direc
519
560
  |---|---|---|
520
561
  | `body` | yes | The captcha image, base64-encoded. |
521
562
  | `comment` | no (recommended) | Hint for the worker, e.g. `"click on the green apple"`. |
522
- | `imgInstructions` | required for `imgType="smart_captcha"` | Instruction image, base64-encoded, showing what to click and in what order. |
563
+ | `imgInstructions` | no | Optional instruction image, base64-encoded. |
523
564
  | `minClicks` | no | Minimum number of clicks expected (default `1`). |
524
565
  | `maxClicks` | no | Maximum number of clicks allowed. |
525
- | `imgType` | no | `"smart_captcha"` to solve a Yandex SmartCaptcha image challenge instead of a generic click captcha. |
526
566
 
527
567
  **Response:** `coordinates` -- a list of `{"x": int, "y": int}` pixel positions to click, in order.
528
568
 
@@ -540,9 +580,6 @@ task = CoordinatesTask(
540
580
  result = client.solve(task)
541
581
  print(result["coordinates"]) # [{"x": 140, "y": 110}]
542
582
  ```
543
- For the Yandex SmartCaptcha image challenge, see
544
- [examples/sync/yandex_smartcaptcha_image.py](https://github.com/captcha-solver-api/python-sdk/blob/main/examples/sync/yandex_smartcaptcha_image.py)
545
- (or [examples/async](https://github.com/captcha-solver-api/python-sdk/blob/main/examples/async/yandex_smartcaptcha_image.py)).
546
583
 
547
584
  ### Tencent
548
585
 
@@ -604,8 +641,8 @@ result = client.solve(task, timeout=300)
604
641
  ```
605
642
 
606
643
  The [API requires at least 5 seconds between polls](https://captcha-solver.com/en/docs/how-it-works).
607
- The SDK defaults to 10 seconds, like the official 2Captcha Python SDK. `solve()`
608
- waits for `polling_interval` before its first poll and between polls.
644
+ The SDK defaults to 10 seconds. `solve()` waits for `polling_interval` before
645
+ its first poll and between polls.
609
646
  Its default 120-second polling window is independent of the API's five-minute
610
647
  task lifetime. A local timeout does not cancel a task or mean that the API
611
648
  failed to solve it. Use `create_task()` and keep its `task_id` if you need to
@@ -695,8 +732,7 @@ Avoid importing it by name: it shadows Python's built-in `TimeoutError`.
695
732
  See the dedicated [examples documentation](https://github.com/captcha-solver-api/python-sdk/blob/main/examples/README.md) for the full
696
733
  sync/async example list, setup steps, expected results, and placeholder guidance.
697
734
 
698
- - **Image/click captchas** (`image_to_text.py`, `coordinates.py`,
699
- `yandex_smartcaptcha_image.py`) run after installing the dependencies and setting a valid
735
+ - **Image/click captchas** (`image_to_text.py`, `coordinates.py`) run after installing the dependencies and setting a valid
700
736
  `CAPTCHA_API_KEY` -- they read sample images bundled in
701
737
  [examples/assets](https://github.com/captcha-solver-api/python-sdk/tree/main/examples/assets), no target page needed.
702
738
  `python-dotenv` is optional: install it only if you want the scripts to load a `.env` file.
@@ -738,6 +774,16 @@ page and session parameters.
738
774
 
739
775
  Full API reference: https://captcha-solver.com/en/docs/captcha-types
740
776
 
777
+ ## Useful Links
778
+
779
+ - [JavaScript SDK](https://github.com/captcha-solver-api/javascript-sdk)
780
+ - [Python examples](https://github.com/captcha-solver-api/python-examples)
781
+ - [Selenium Python examples](https://github.com/captcha-solver-api/captcha-solver-selenium-python-examples)
782
+ - [JavaScript examples](https://github.com/captcha-solver-api/javascript-examples)
783
+ - [Tencent CAPTCHA automation examples](https://github.com/captcha-solver-api/How-to-Automate-Tencent-CAPTCHA)
784
+ - [Cloudflare Turnstile Puppeteer Demo](https://github.com/captcha-solver-api/cloudflare-turnstile-puppeteer-demo) — a working browser automation example for Cloudflare Turnstile.
785
+ - [How to Automate Tencent CAPTCHA](https://captcha-solver.com/en/blog/how-to-automate-tencent-captcha) — a step-by-step guide with Python and JavaScript SDK examples.
786
+
741
787
  ## License
742
788
 
743
789
  This project is licensed under the MIT License. See [LICENSE.md](https://github.com/captcha-solver-api/python-sdk/blob/main/LICENSE.md) for details.
@@ -1,6 +1,18 @@
1
1
  # Captcha Solver Python SDK
2
2
 
3
- ![python-sdk-banner](https://raw.githubusercontent.com/captcha-solver-api/python-sdk/main/assets/repo-banner-python.png)
3
+ ![python-sdk-banner](https://raw.githubusercontent.com/captcha-solver-api/python-sdk/main/assets/repo-banner-python.png?v=09a4ebb)
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/captcha-solver-api?logo=pypi&logoColor=white)](https://pypi.org/project/captcha-solver-api/)
6
+ [![Python](https://img.shields.io/pypi/pyversions/captcha-solver-api?logo=python&logoColor=white)](https://pypi.org/project/captcha-solver-api/)
7
+ [![Downloads](https://img.shields.io/pepy/dt/captcha-solver-api)](https://pepy.tech/project/captcha-solver-api)
8
+ [![Tests](https://github.com/captcha-solver-api/python-sdk/actions/workflows/tests.yml/badge.svg)](https://github.com/captcha-solver-api/python-sdk/actions/workflows/tests.yml)
9
+ [![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/dzmitry-duboyski/77b084ed0d46c740183f89ec66af8a95/raw/python-sdk-coverage.json)](https://github.com/captcha-solver-api/python-sdk/actions/workflows/tests.yml)
10
+ [![Typed](https://img.shields.io/badge/typing-typed-blue)](https://github.com/captcha-solver-api/python-sdk/blob/main/captcha_solver_api/py.typed)
11
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/captcha-solver-api/python-sdk/blob/main/LICENSE.md)
12
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
13
+ [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
14
+ [![GitHub Release](https://img.shields.io/github/v/release/captcha-solver-api/python-sdk)](https://github.com/captcha-solver-api/python-sdk/releases)
15
+ [![JavaScript SDK](https://img.shields.io/badge/also_available-JavaScript_SDK-f7df1e?logo=javascript&logoColor=black)](https://github.com/captcha-solver-api/javascript-sdk)
4
16
 
5
17
  Official Python SDK for the Captcha Solver API. Solve reCAPTCHA v2/v3, Cloudflare Turnstile, GeeTest, Yandex SmartCaptcha, Tencent, and image/click captchas with a single method call -- sync or async.
6
18
 
@@ -11,6 +23,7 @@ Full API reference (all endpoints, error codes, captcha-type details): **https:/
11
23
  - [Installation](#installation)
12
24
  - [Configuration](#configuration)
13
25
  - [Quick Start](#quick-start)
26
+ - [Build Faster with AI](#build-faster-with-ai)
14
27
  - [Supported CAPTCHA Types](#supported-captcha-types)
15
28
  - [Client Reference](#client-reference)
16
29
  - [CaptchaClient(...)](#captchaclient)
@@ -38,6 +51,7 @@ Full API reference (all endpoints, error codes, captcha-type details): **https:/
38
51
  - [Running the examples](#running-the-examples)
39
52
  - [Requirements](#requirements)
40
53
  - [API Documentation](#api-documentation)
54
+ - [Useful Links](#useful-links)
41
55
  - [License](#license)
42
56
 
43
57
  ## Installation
@@ -88,6 +102,23 @@ result = client.solve(task)
88
102
  print(result["gRecaptchaResponse"])
89
103
  ```
90
104
 
105
+ ## Build Faster with AI
106
+
107
+ Use our [`llms.txt`](https://captcha-solver.com/llms.txt) as context when asking an
108
+ AI assistant to build or update your integration. It contains the current API
109
+ format, supported task parameters, SDK installation instructions, solution
110
+ fields, and error codes in one file.
111
+
112
+ Copy this prompt and add a description of the page and CAPTCHA you need to
113
+ handle:
114
+
115
+ ```text
116
+ Read https://captcha-solver.com/llms.txt and use the Captcha Solver Python SDK.
117
+ Write a complete integration for this task: [describe your task here].
118
+ Use the documented task parameters and show how to apply the returned solution
119
+ on the target page.
120
+ ```
121
+
91
122
  ## Supported CAPTCHA Types
92
123
 
93
124
  | Type | Proxyless | With Proxy |
@@ -121,7 +152,7 @@ Constructor.
121
152
  | `client_key` | `str` | required | Your Captcha Solver API key. Raises `ValidationError` if empty. |
122
153
  | `base_url` | `str` | `https://api.captcha-solver.com` | API base URL. Override only for self-hosted/staging deployments. |
123
154
  | `timeout` | `int` | `120` | Polling window after task creation, in seconds. Overridable per call; each HTTP request has a separate 30-second timeout. |
124
- | `polling_interval` | `int` | `10` | Seconds before the first `getTaskResult` poll and between subsequent polls, matching the official 2Captcha Python SDK. |
155
+ | `polling_interval` | `int` | `10` | Seconds before the first `getTaskResult` poll and between subsequent polls. |
125
156
  | `language_pool` | `Optional[str]` | `None` | Default worker pool (`"en"` or `"ru"`) applied to every call that doesn't pass its own `language_pool`. |
126
157
 
127
158
  Both clients hold a reusable connection pool (`requests.Session` / `httpx.AsyncClient`)
@@ -317,12 +348,13 @@ that the target page expects in `cf-turnstile-response`.
317
348
  | `action` | no | Value of the widget's `data-action` attribute, if set. |
318
349
  | `data` | no | Custom payload from the widget's `data-cdata` attribute, if set. |
319
350
  | `pagedata` | no | Value of the `chlPageData` parameter, needed for some Cloudflare challenge pages beyond the basic widget. |
320
- | `userAgent` | no | User-Agent to solve with -- the returned token is tied to it, submit with the same one. |
351
+ | `userAgent` | no | Current browser User-Agent. Pass it for Cloudflare Challenge pages; it is not needed for a standalone widget. |
321
352
 
322
353
  **Response:** `token` -- submit as `cf-turnstile-response`. For Cloudflare
323
- Challenge pages, the solution also includes `userAgent`; use that User-Agent
324
- when submitting the token. Pass `action`, `data`, and `pagedata` when the page
325
- provides them. The API field is spelled `pagedata`, all lowercase.
354
+ Challenge pages, pass `action`, `data`, `pagedata`, and the current browser
355
+ `userAgent` in the task. The solution also contains `userAgent`; switch the
356
+ browser or HTTP client to this returned value before invoking the callback with
357
+ the token. The API field is spelled `pagedata`, all lowercase.
326
358
 
327
359
  ```python
328
360
  from captcha_solver_api import CaptchaClient
@@ -330,10 +362,15 @@ from captcha_solver_api.tasks import TurnstileTaskProxyless
330
362
  client = CaptchaClient("your_api_key")
331
363
  task = TurnstileTaskProxyless(
332
364
  websiteURL="https://example.com/login",
333
- websiteKey="YOUR_WEBSITE_KEY"
365
+ websiteKey="YOUR_WEBSITE_KEY",
366
+ action="managed",
367
+ data="INTERCEPTED_CDATA",
368
+ pagedata="INTERCEPTED_CHL_PAGE_DATA",
369
+ userAgent="CURRENT_BROWSER_USER_AGENT",
334
370
  )
335
371
  result = client.solve(task)
336
372
  print(result["token"])
373
+ print(result.get("userAgent"))
337
374
  ```
338
375
 
339
376
  With proxy, use `TurnstileTask` (same proxy fields as reCAPTCHA v2).
@@ -440,7 +477,7 @@ for the widget on the target page. Use the coordinates method for image challeng
440
477
 
441
478
  `YandexSmartCaptchaTaskProxyless` / `YandexSmartCaptchaTask` -- token-based
442
479
  challenge. For the image challenge instead, use `CoordinatesTask` with
443
- `imgType="smart_captcha"` (see [Coordinates](#coordinates-click-captcha) below).
480
+ [Coordinates](#coordinates-click-captcha) is available for generic click captchas.
444
481
 
445
482
  | Parameter | Required | Description |
446
483
  |---|---|---|
@@ -483,10 +520,9 @@ SmartCaptcha's image challenge. No proxy variant -- the image is submitted direc
483
520
  |---|---|---|
484
521
  | `body` | yes | The captcha image, base64-encoded. |
485
522
  | `comment` | no (recommended) | Hint for the worker, e.g. `"click on the green apple"`. |
486
- | `imgInstructions` | required for `imgType="smart_captcha"` | Instruction image, base64-encoded, showing what to click and in what order. |
523
+ | `imgInstructions` | no | Optional instruction image, base64-encoded. |
487
524
  | `minClicks` | no | Minimum number of clicks expected (default `1`). |
488
525
  | `maxClicks` | no | Maximum number of clicks allowed. |
489
- | `imgType` | no | `"smart_captcha"` to solve a Yandex SmartCaptcha image challenge instead of a generic click captcha. |
490
526
 
491
527
  **Response:** `coordinates` -- a list of `{"x": int, "y": int}` pixel positions to click, in order.
492
528
 
@@ -504,9 +540,6 @@ task = CoordinatesTask(
504
540
  result = client.solve(task)
505
541
  print(result["coordinates"]) # [{"x": 140, "y": 110}]
506
542
  ```
507
- For the Yandex SmartCaptcha image challenge, see
508
- [examples/sync/yandex_smartcaptcha_image.py](https://github.com/captcha-solver-api/python-sdk/blob/main/examples/sync/yandex_smartcaptcha_image.py)
509
- (or [examples/async](https://github.com/captcha-solver-api/python-sdk/blob/main/examples/async/yandex_smartcaptcha_image.py)).
510
543
 
511
544
  ### Tencent
512
545
 
@@ -568,8 +601,8 @@ result = client.solve(task, timeout=300)
568
601
  ```
569
602
 
570
603
  The [API requires at least 5 seconds between polls](https://captcha-solver.com/en/docs/how-it-works).
571
- The SDK defaults to 10 seconds, like the official 2Captcha Python SDK. `solve()`
572
- waits for `polling_interval` before its first poll and between polls.
604
+ The SDK defaults to 10 seconds. `solve()` waits for `polling_interval` before
605
+ its first poll and between polls.
573
606
  Its default 120-second polling window is independent of the API's five-minute
574
607
  task lifetime. A local timeout does not cancel a task or mean that the API
575
608
  failed to solve it. Use `create_task()` and keep its `task_id` if you need to
@@ -659,8 +692,7 @@ Avoid importing it by name: it shadows Python's built-in `TimeoutError`.
659
692
  See the dedicated [examples documentation](https://github.com/captcha-solver-api/python-sdk/blob/main/examples/README.md) for the full
660
693
  sync/async example list, setup steps, expected results, and placeholder guidance.
661
694
 
662
- - **Image/click captchas** (`image_to_text.py`, `coordinates.py`,
663
- `yandex_smartcaptcha_image.py`) run after installing the dependencies and setting a valid
695
+ - **Image/click captchas** (`image_to_text.py`, `coordinates.py`) run after installing the dependencies and setting a valid
664
696
  `CAPTCHA_API_KEY` -- they read sample images bundled in
665
697
  [examples/assets](https://github.com/captcha-solver-api/python-sdk/tree/main/examples/assets), no target page needed.
666
698
  `python-dotenv` is optional: install it only if you want the scripts to load a `.env` file.
@@ -702,6 +734,16 @@ page and session parameters.
702
734
 
703
735
  Full API reference: https://captcha-solver.com/en/docs/captcha-types
704
736
 
737
+ ## Useful Links
738
+
739
+ - [JavaScript SDK](https://github.com/captcha-solver-api/javascript-sdk)
740
+ - [Python examples](https://github.com/captcha-solver-api/python-examples)
741
+ - [Selenium Python examples](https://github.com/captcha-solver-api/captcha-solver-selenium-python-examples)
742
+ - [JavaScript examples](https://github.com/captcha-solver-api/javascript-examples)
743
+ - [Tencent CAPTCHA automation examples](https://github.com/captcha-solver-api/How-to-Automate-Tencent-CAPTCHA)
744
+ - [Cloudflare Turnstile Puppeteer Demo](https://github.com/captcha-solver-api/cloudflare-turnstile-puppeteer-demo) — a working browser automation example for Cloudflare Turnstile.
745
+ - [How to Automate Tencent CAPTCHA](https://captcha-solver.com/en/blog/how-to-automate-tencent-captcha) — a step-by-step guide with Python and JavaScript SDK examples.
746
+
705
747
  ## License
706
748
 
707
749
  This project is licensed under the MIT License. See [LICENSE.md](https://github.com/captcha-solver-api/python-sdk/blob/main/LICENSE.md) for details.
@@ -13,26 +13,26 @@ Example:
13
13
  result = client.solve(task)
14
14
  """
15
15
 
16
- from .client import CaptchaClient
16
+ from ._version import __version__
17
17
  from .async_client import AsyncCaptchaClient
18
+ from .client import CaptchaClient
18
19
  from .exceptions import (
19
- CaptchaError,
20
20
  ApiError,
21
- NetworkError,
21
+ CaptchaError,
22
22
  CaptchaTimeoutError,
23
+ NetworkError,
23
24
  TimeoutError,
24
25
  ValidationError,
25
26
  )
26
27
 
27
- from ._version import __version__
28
-
29
28
  __all__ = [
30
- "CaptchaClient",
29
+ "ApiError",
31
30
  "AsyncCaptchaClient",
31
+ "CaptchaClient",
32
32
  "CaptchaError",
33
- "ApiError",
34
- "NetworkError",
35
33
  "CaptchaTimeoutError",
34
+ "NetworkError",
36
35
  "TimeoutError",
37
36
  "ValidationError",
37
+ "__version__",
38
38
  ]
@@ -0,0 +1 @@
1
+ __version__ = "1.0.3"
@@ -15,8 +15,8 @@ import httpx
15
15
  from ._version import __version__
16
16
  from .exceptions import (
17
17
  ApiError,
18
- NetworkError,
19
18
  CaptchaTimeoutError,
19
+ NetworkError,
20
20
  ValidationError,
21
21
  )
22
22
 
@@ -54,7 +54,7 @@ class AsyncCaptchaClient:
54
54
  raising `CaptchaTimeoutError`. Can be overridden per call.
55
55
  polling_interval: Seconds to wait before the first `getTaskResult`
56
56
  poll and between subsequent polls inside `solve()`. Defaults
57
- to 10 seconds, matching the official 2Captcha Python SDK.
57
+ to 10 seconds.
58
58
  language_pool: Default worker pool selector (e.g. `"en"` or `"ru"`)
59
59
  applied to every `create_task()`/`solve()` call that doesn't pass
60
60
  its own `language_pool`. Leave unset to use the account's default
@@ -87,7 +87,7 @@ class AsyncCaptchaClient:
87
87
  (`async with AsyncCaptchaClient(...) as c:`) to have it closed automatically."""
88
88
  await self._client.aclose()
89
89
 
90
- async def __aenter__(self) -> "AsyncCaptchaClient":
90
+ async def __aenter__(self) -> AsyncCaptchaClient:
91
91
  return self
92
92
 
93
93
  async def __aexit__(self, *exc_info: Any) -> None:
@@ -113,6 +113,8 @@ class AsyncCaptchaClient:
113
113
  except ValueError as exc:
114
114
  raise NetworkError(f"Non-JSON response from API: {response.text[:200]!r}") from exc
115
115
 
116
+ if not isinstance(data, dict):
117
+ raise NetworkError(f"Unexpected API response shape: {type(data).__name__}")
116
118
  return data
117
119
 
118
120
  def _ensure_success(self, data: Dict[str, Any]) -> None:
@@ -154,7 +156,7 @@ class AsyncCaptchaClient:
154
156
 
155
157
  data = await self._request("createTask", payload)
156
158
  self._ensure_success(data)
157
- return data["taskId"]
159
+ return int(data["taskId"])
158
160
 
159
161
  async def get_task_result(self, task_id: int) -> Dict[str, Any]:
160
162
  """Fetch the current status of a previously submitted task.
@@ -203,7 +205,7 @@ class AsyncCaptchaClient:
203
205
  payload = {"clientKey": self.client_key}
204
206
  data = await self._request("getBalance", payload)
205
207
  self._ensure_success(data)
206
- return data["balance"]
208
+ return float(data["balance"])
207
209
 
208
210
  async def solve(
209
211
  self,
@@ -248,6 +250,9 @@ class AsyncCaptchaClient:
248
250
  result = await self.get_task_result(task_id)
249
251
 
250
252
  if result.get("status") == "ready":
251
- return result["solution"]
253
+ solution = result["solution"]
254
+ if not isinstance(solution, dict):
255
+ raise NetworkError("API returned a ready task without a solution object")
256
+ return solution
252
257
 
253
258
  raise CaptchaTimeoutError("Task solving timed out.")
@@ -12,8 +12,8 @@ import requests
12
12
  from ._version import __version__
13
13
  from .exceptions import (
14
14
  ApiError,
15
- NetworkError,
16
15
  CaptchaTimeoutError,
16
+ NetworkError,
17
17
  ValidationError,
18
18
  )
19
19
 
@@ -43,7 +43,7 @@ class CaptchaClient:
43
43
  raising `CaptchaTimeoutError`. Can be overridden per call.
44
44
  polling_interval: Seconds to wait before the first `getTaskResult`
45
45
  poll and between subsequent polls inside `solve()`. Defaults
46
- to 10 seconds, matching the official 2Captcha Python SDK.
46
+ to 10 seconds.
47
47
  language_pool: Default worker pool selector (e.g. `"en"` or `"ru"`)
48
48
  applied to every `create_task()`/`solve()` call that doesn't pass
49
49
  its own `language_pool`. Leave unset to use the account's default
@@ -76,7 +76,7 @@ class CaptchaClient:
76
76
  to have it closed automatically."""
77
77
  self.session.close()
78
78
 
79
- def __enter__(self) -> "CaptchaClient":
79
+ def __enter__(self) -> CaptchaClient:
80
80
  return self
81
81
 
82
82
  def __exit__(self, *exc_info: Any) -> None:
@@ -102,6 +102,8 @@ class CaptchaClient:
102
102
  except ValueError as exc:
103
103
  raise NetworkError(f"Non-JSON response from API: {response.text[:200]!r}") from exc
104
104
 
105
+ if not isinstance(data, dict):
106
+ raise NetworkError(f"Unexpected API response shape: {type(data).__name__}")
105
107
  return data
106
108
 
107
109
  def _ensure_success(self, data: Dict[str, Any]) -> None:
@@ -141,7 +143,7 @@ class CaptchaClient:
141
143
 
142
144
  data = self._request("createTask", payload)
143
145
  self._ensure_success(data)
144
- return data["taskId"]
146
+ return int(data["taskId"])
145
147
 
146
148
  def get_task_result(self, task_id: int) -> Dict[str, Any]:
147
149
  """Fetches the current status of a task created with `create_task()`.
@@ -187,7 +189,7 @@ class CaptchaClient:
187
189
  payload = {"clientKey": self.client_key}
188
190
  data = self._request("getBalance", payload)
189
191
  self._ensure_success(data)
190
- return data["balance"]
192
+ return float(data["balance"])
191
193
 
192
194
  def solve(
193
195
  self,
@@ -230,6 +232,9 @@ class CaptchaClient:
230
232
  result = self.get_task_result(task_id)
231
233
 
232
234
  if result.get("status") == "ready":
233
- return result["solution"]
235
+ solution = result["solution"]
236
+ if not isinstance(solution, dict):
237
+ raise NetworkError("API returned a ready task without a solution object")
238
+ return solution
234
239
 
235
240
  raise CaptchaTimeoutError("Task solving timed out.")
@@ -6,20 +6,14 @@ Custom exceptions used by the Captcha Solver SDK.
6
6
  class CaptchaError(Exception):
7
7
  """Base exception for all SDK errors."""
8
8
 
9
- pass
10
-
11
9
 
12
10
  class NetworkError(CaptchaError):
13
11
  """Raised when a network request fails."""
14
12
 
15
- pass
16
-
17
13
 
18
14
  class CaptchaTimeoutError(CaptchaError):
19
15
  """Raised when the operation exceeds the configured timeout."""
20
16
 
21
- pass
22
-
23
17
 
24
18
  # Deprecated alias kept for backward compatibility. It shadows the built-in
25
19
  # ``TimeoutError`` when imported by name, so prefer ``CaptchaTimeoutError``.
@@ -37,5 +31,3 @@ class ApiError(CaptchaError):
37
31
 
38
32
  class ValidationError(CaptchaError):
39
33
  """Raised for client-side argument problems caught before any request is sent."""
40
-
41
- pass
@@ -12,7 +12,7 @@ once the task is `"ready"`.
12
12
 
13
13
  from __future__ import annotations
14
14
 
15
- from typing import Any, Dict, List, Optional
15
+ from typing import Any, Dict, Optional
16
16
 
17
17
 
18
18
  class BaseTask:
@@ -55,6 +55,15 @@ class ProxyMixin:
55
55
  proxy_login: Optional[str] = None,
56
56
  proxy_password: Optional[str] = None,
57
57
  ) -> None:
58
+ """Sets the proxy fields on this task.
59
+
60
+ Args:
61
+ proxy_type: `"http"`, `"socks4"`, or `"socks5"`.
62
+ proxy_address: Proxy IP address or hostname.
63
+ proxy_port: Proxy port.
64
+ proxy_login: Proxy auth username, if required.
65
+ proxy_password: Proxy auth password, if required.
66
+ """
58
67
  self.proxyType = proxy_type
59
68
  self.proxyAddress = proxy_address
60
69
  self.proxyPort = proxy_port
@@ -294,11 +303,14 @@ class TurnstileTaskProxyless(BaseTask):
294
303
  challenge pages beyond the basic widget. Named lowercase (not `pageData`)
295
304
  to match the API field name exactly -- Cloudflare-specific fields are
296
305
  the one place this API doesn't camelCase.
297
- userAgent: User-Agent to solve with. The returned token is tied to it --
298
- submit it with the same User-Agent.
306
+ userAgent: User-Agent of the browser that loaded the challenge. Pass it
307
+ together with `action`, `data`, and `pagedata` for Cloudflare
308
+ Challenge pages. It is not needed for a standalone widget.
299
309
 
300
310
  Returns (`solution` from `solve()`):
301
311
  `token` -- the value to submit as `cf-turnstile-response`.
312
+ `userAgent` -- for Cloudflare Challenge pages, switch the browser or HTTP
313
+ client to this returned User-Agent before invoking the callback.
302
314
  """
303
315
 
304
316
  type = "TurnstileTaskProxyless"
@@ -336,10 +348,14 @@ class TurnstileTask(BaseTask, ProxyMixin):
336
348
  pagedata: Value of the `chlPageData` parameter, needed for some Cloudflare
337
349
  challenge pages beyond the basic widget. Named lowercase (not `pageData`)
338
350
  to match the API field name exactly.
339
- userAgent: User-Agent to solve with. The returned token is tied to it.
351
+ userAgent: User-Agent of the browser that loaded the challenge. Pass it
352
+ for Cloudflare Challenge pages; it is not needed for a standalone
353
+ widget.
340
354
 
341
355
  Returns (`solution` from `solve()`):
342
356
  `token` -- the value to submit as `cf-turnstile-response`.
357
+ `userAgent` -- for Cloudflare Challenge pages, switch the browser or HTTP
358
+ client to this returned User-Agent before invoking the callback.
343
359
  """
344
360
 
345
361
  type = "TurnstileTask"
@@ -525,8 +541,7 @@ class GeeTestTask(BaseTask, ProxyMixin):
525
541
 
526
542
 
527
543
  class YandexSmartCaptchaTaskProxyless(BaseTask):
528
- """Yandex SmartCaptcha (token challenge) without proxy. For the image
529
- challenge instead, use `CoordinatesTask` with `imgType="smart_captcha"`.
544
+ """Yandex SmartCaptcha (token challenge) without proxy.
530
545
 
531
546
  Args:
532
547
  websiteURL: Full URL of the page where the widget is located.
@@ -597,20 +612,16 @@ class YandexSmartCaptchaTask(BaseTask, ProxyMixin):
597
612
 
598
613
 
599
614
  class CoordinatesTask(BaseTask):
600
- """Coordinate-based (click) image captcha. Used both for generic
601
- "click on X" captchas and for Yandex SmartCaptcha's image challenge (set
602
- `imgType` accordingly). No proxy variant -- the image is submitted directly.
615
+ """Coordinate-based (click) image captcha. No proxy variant -- the image
616
+ is submitted directly.
603
617
 
604
618
  Args:
605
619
  body: The captcha image, base64-encoded (no `data:image/...;base64,` prefix).
606
620
  comment: Hint for the worker, e.g. `"click on the green apple"`. Recommended
607
621
  for generic click captchas.
608
- imgInstructions: Optional instruction image, base64-encoded, showing what
609
- to click and in what order. Required when `imgType="smart_captcha"`.
622
+ imgInstructions: Optional instruction image, base64-encoded.
610
623
  minClicks: Minimum number of clicks expected (default `1`).
611
624
  maxClicks: Maximum number of clicks allowed.
612
- imgType: Set to `"smart_captcha"` to solve a Yandex SmartCaptcha image
613
- challenge instead of a generic click captcha.
614
625
 
615
626
  Returns (`solution` from `solve()`):
616
627
  `coordinates` -- a list of `{"x": int, "y": int}` pixel positions to click,
@@ -626,14 +637,12 @@ class CoordinatesTask(BaseTask):
626
637
  imgInstructions: Optional[str] = None,
627
638
  minClicks: Optional[int] = None,
628
639
  maxClicks: Optional[int] = None,
629
- imgType: Optional[str] = None,
630
640
  ) -> None:
631
641
  self.body = body
632
642
  self.comment = comment
633
643
  self.imgInstructions = imgInstructions
634
644
  self.minClicks = minClicks
635
645
  self.maxClicks = maxClicks
636
- self.imgType = imgType
637
646
 
638
647
 
639
648
  class TencentTaskProxyless(BaseTask):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: captcha-solver-api
3
- Version: 1.0.1
3
+ Version: 1.0.3
4
4
  Summary: Official Python SDK for solving reCAPTCHA v2/v3, Cloudflare Turnstile, GeeTest v3/v4, Yandex SmartCaptcha, Tencent, and image/click captchas via the Captcha Solver API
5
5
  Author: Captcha Solver Team
6
6
  License-Expression: MIT
@@ -32,11 +32,27 @@ Requires-Dist: pytest>=7; extra == "dev"
32
32
  Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
33
33
  Requires-Dist: pytest-mock>=3; extra == "dev"
34
34
  Requires-Dist: python-dotenv>=1.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.6; extra == "dev"
36
+ Requires-Dist: mypy>=1.10; extra == "dev"
37
+ Requires-Dist: types-requests>=2.28; extra == "dev"
38
+ Requires-Dist: pytest-cov>=4; extra == "dev"
35
39
  Dynamic: license-file
36
40
 
37
41
  # Captcha Solver Python SDK
38
42
 
39
- ![python-sdk-banner](https://raw.githubusercontent.com/captcha-solver-api/python-sdk/main/assets/repo-banner-python.png)
43
+ ![python-sdk-banner](https://raw.githubusercontent.com/captcha-solver-api/python-sdk/main/assets/repo-banner-python.png?v=09a4ebb)
44
+
45
+ [![PyPI](https://img.shields.io/pypi/v/captcha-solver-api?logo=pypi&logoColor=white)](https://pypi.org/project/captcha-solver-api/)
46
+ [![Python](https://img.shields.io/pypi/pyversions/captcha-solver-api?logo=python&logoColor=white)](https://pypi.org/project/captcha-solver-api/)
47
+ [![Downloads](https://img.shields.io/pepy/dt/captcha-solver-api)](https://pepy.tech/project/captcha-solver-api)
48
+ [![Tests](https://github.com/captcha-solver-api/python-sdk/actions/workflows/tests.yml/badge.svg)](https://github.com/captcha-solver-api/python-sdk/actions/workflows/tests.yml)
49
+ [![Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/dzmitry-duboyski/77b084ed0d46c740183f89ec66af8a95/raw/python-sdk-coverage.json)](https://github.com/captcha-solver-api/python-sdk/actions/workflows/tests.yml)
50
+ [![Typed](https://img.shields.io/badge/typing-typed-blue)](https://github.com/captcha-solver-api/python-sdk/blob/main/captcha_solver_api/py.typed)
51
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/captcha-solver-api/python-sdk/blob/main/LICENSE.md)
52
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
53
+ [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
54
+ [![GitHub Release](https://img.shields.io/github/v/release/captcha-solver-api/python-sdk)](https://github.com/captcha-solver-api/python-sdk/releases)
55
+ [![JavaScript SDK](https://img.shields.io/badge/also_available-JavaScript_SDK-f7df1e?logo=javascript&logoColor=black)](https://github.com/captcha-solver-api/javascript-sdk)
40
56
 
41
57
  Official Python SDK for the Captcha Solver API. Solve reCAPTCHA v2/v3, Cloudflare Turnstile, GeeTest, Yandex SmartCaptcha, Tencent, and image/click captchas with a single method call -- sync or async.
42
58
 
@@ -47,6 +63,7 @@ Full API reference (all endpoints, error codes, captcha-type details): **https:/
47
63
  - [Installation](#installation)
48
64
  - [Configuration](#configuration)
49
65
  - [Quick Start](#quick-start)
66
+ - [Build Faster with AI](#build-faster-with-ai)
50
67
  - [Supported CAPTCHA Types](#supported-captcha-types)
51
68
  - [Client Reference](#client-reference)
52
69
  - [CaptchaClient(...)](#captchaclient)
@@ -74,6 +91,7 @@ Full API reference (all endpoints, error codes, captcha-type details): **https:/
74
91
  - [Running the examples](#running-the-examples)
75
92
  - [Requirements](#requirements)
76
93
  - [API Documentation](#api-documentation)
94
+ - [Useful Links](#useful-links)
77
95
  - [License](#license)
78
96
 
79
97
  ## Installation
@@ -124,6 +142,23 @@ result = client.solve(task)
124
142
  print(result["gRecaptchaResponse"])
125
143
  ```
126
144
 
145
+ ## Build Faster with AI
146
+
147
+ Use our [`llms.txt`](https://captcha-solver.com/llms.txt) as context when asking an
148
+ AI assistant to build or update your integration. It contains the current API
149
+ format, supported task parameters, SDK installation instructions, solution
150
+ fields, and error codes in one file.
151
+
152
+ Copy this prompt and add a description of the page and CAPTCHA you need to
153
+ handle:
154
+
155
+ ```text
156
+ Read https://captcha-solver.com/llms.txt and use the Captcha Solver Python SDK.
157
+ Write a complete integration for this task: [describe your task here].
158
+ Use the documented task parameters and show how to apply the returned solution
159
+ on the target page.
160
+ ```
161
+
127
162
  ## Supported CAPTCHA Types
128
163
 
129
164
  | Type | Proxyless | With Proxy |
@@ -157,7 +192,7 @@ Constructor.
157
192
  | `client_key` | `str` | required | Your Captcha Solver API key. Raises `ValidationError` if empty. |
158
193
  | `base_url` | `str` | `https://api.captcha-solver.com` | API base URL. Override only for self-hosted/staging deployments. |
159
194
  | `timeout` | `int` | `120` | Polling window after task creation, in seconds. Overridable per call; each HTTP request has a separate 30-second timeout. |
160
- | `polling_interval` | `int` | `10` | Seconds before the first `getTaskResult` poll and between subsequent polls, matching the official 2Captcha Python SDK. |
195
+ | `polling_interval` | `int` | `10` | Seconds before the first `getTaskResult` poll and between subsequent polls. |
161
196
  | `language_pool` | `Optional[str]` | `None` | Default worker pool (`"en"` or `"ru"`) applied to every call that doesn't pass its own `language_pool`. |
162
197
 
163
198
  Both clients hold a reusable connection pool (`requests.Session` / `httpx.AsyncClient`)
@@ -353,12 +388,13 @@ that the target page expects in `cf-turnstile-response`.
353
388
  | `action` | no | Value of the widget's `data-action` attribute, if set. |
354
389
  | `data` | no | Custom payload from the widget's `data-cdata` attribute, if set. |
355
390
  | `pagedata` | no | Value of the `chlPageData` parameter, needed for some Cloudflare challenge pages beyond the basic widget. |
356
- | `userAgent` | no | User-Agent to solve with -- the returned token is tied to it, submit with the same one. |
391
+ | `userAgent` | no | Current browser User-Agent. Pass it for Cloudflare Challenge pages; it is not needed for a standalone widget. |
357
392
 
358
393
  **Response:** `token` -- submit as `cf-turnstile-response`. For Cloudflare
359
- Challenge pages, the solution also includes `userAgent`; use that User-Agent
360
- when submitting the token. Pass `action`, `data`, and `pagedata` when the page
361
- provides them. The API field is spelled `pagedata`, all lowercase.
394
+ Challenge pages, pass `action`, `data`, `pagedata`, and the current browser
395
+ `userAgent` in the task. The solution also contains `userAgent`; switch the
396
+ browser or HTTP client to this returned value before invoking the callback with
397
+ the token. The API field is spelled `pagedata`, all lowercase.
362
398
 
363
399
  ```python
364
400
  from captcha_solver_api import CaptchaClient
@@ -366,10 +402,15 @@ from captcha_solver_api.tasks import TurnstileTaskProxyless
366
402
  client = CaptchaClient("your_api_key")
367
403
  task = TurnstileTaskProxyless(
368
404
  websiteURL="https://example.com/login",
369
- websiteKey="YOUR_WEBSITE_KEY"
405
+ websiteKey="YOUR_WEBSITE_KEY",
406
+ action="managed",
407
+ data="INTERCEPTED_CDATA",
408
+ pagedata="INTERCEPTED_CHL_PAGE_DATA",
409
+ userAgent="CURRENT_BROWSER_USER_AGENT",
370
410
  )
371
411
  result = client.solve(task)
372
412
  print(result["token"])
413
+ print(result.get("userAgent"))
373
414
  ```
374
415
 
375
416
  With proxy, use `TurnstileTask` (same proxy fields as reCAPTCHA v2).
@@ -476,7 +517,7 @@ for the widget on the target page. Use the coordinates method for image challeng
476
517
 
477
518
  `YandexSmartCaptchaTaskProxyless` / `YandexSmartCaptchaTask` -- token-based
478
519
  challenge. For the image challenge instead, use `CoordinatesTask` with
479
- `imgType="smart_captcha"` (see [Coordinates](#coordinates-click-captcha) below).
520
+ [Coordinates](#coordinates-click-captcha) is available for generic click captchas.
480
521
 
481
522
  | Parameter | Required | Description |
482
523
  |---|---|---|
@@ -519,10 +560,9 @@ SmartCaptcha's image challenge. No proxy variant -- the image is submitted direc
519
560
  |---|---|---|
520
561
  | `body` | yes | The captcha image, base64-encoded. |
521
562
  | `comment` | no (recommended) | Hint for the worker, e.g. `"click on the green apple"`. |
522
- | `imgInstructions` | required for `imgType="smart_captcha"` | Instruction image, base64-encoded, showing what to click and in what order. |
563
+ | `imgInstructions` | no | Optional instruction image, base64-encoded. |
523
564
  | `minClicks` | no | Minimum number of clicks expected (default `1`). |
524
565
  | `maxClicks` | no | Maximum number of clicks allowed. |
525
- | `imgType` | no | `"smart_captcha"` to solve a Yandex SmartCaptcha image challenge instead of a generic click captcha. |
526
566
 
527
567
  **Response:** `coordinates` -- a list of `{"x": int, "y": int}` pixel positions to click, in order.
528
568
 
@@ -540,9 +580,6 @@ task = CoordinatesTask(
540
580
  result = client.solve(task)
541
581
  print(result["coordinates"]) # [{"x": 140, "y": 110}]
542
582
  ```
543
- For the Yandex SmartCaptcha image challenge, see
544
- [examples/sync/yandex_smartcaptcha_image.py](https://github.com/captcha-solver-api/python-sdk/blob/main/examples/sync/yandex_smartcaptcha_image.py)
545
- (or [examples/async](https://github.com/captcha-solver-api/python-sdk/blob/main/examples/async/yandex_smartcaptcha_image.py)).
546
583
 
547
584
  ### Tencent
548
585
 
@@ -604,8 +641,8 @@ result = client.solve(task, timeout=300)
604
641
  ```
605
642
 
606
643
  The [API requires at least 5 seconds between polls](https://captcha-solver.com/en/docs/how-it-works).
607
- The SDK defaults to 10 seconds, like the official 2Captcha Python SDK. `solve()`
608
- waits for `polling_interval` before its first poll and between polls.
644
+ The SDK defaults to 10 seconds. `solve()` waits for `polling_interval` before
645
+ its first poll and between polls.
609
646
  Its default 120-second polling window is independent of the API's five-minute
610
647
  task lifetime. A local timeout does not cancel a task or mean that the API
611
648
  failed to solve it. Use `create_task()` and keep its `task_id` if you need to
@@ -695,8 +732,7 @@ Avoid importing it by name: it shadows Python's built-in `TimeoutError`.
695
732
  See the dedicated [examples documentation](https://github.com/captcha-solver-api/python-sdk/blob/main/examples/README.md) for the full
696
733
  sync/async example list, setup steps, expected results, and placeholder guidance.
697
734
 
698
- - **Image/click captchas** (`image_to_text.py`, `coordinates.py`,
699
- `yandex_smartcaptcha_image.py`) run after installing the dependencies and setting a valid
735
+ - **Image/click captchas** (`image_to_text.py`, `coordinates.py`) run after installing the dependencies and setting a valid
700
736
  `CAPTCHA_API_KEY` -- they read sample images bundled in
701
737
  [examples/assets](https://github.com/captcha-solver-api/python-sdk/tree/main/examples/assets), no target page needed.
702
738
  `python-dotenv` is optional: install it only if you want the scripts to load a `.env` file.
@@ -738,6 +774,16 @@ page and session parameters.
738
774
 
739
775
  Full API reference: https://captcha-solver.com/en/docs/captcha-types
740
776
 
777
+ ## Useful Links
778
+
779
+ - [JavaScript SDK](https://github.com/captcha-solver-api/javascript-sdk)
780
+ - [Python examples](https://github.com/captcha-solver-api/python-examples)
781
+ - [Selenium Python examples](https://github.com/captcha-solver-api/captcha-solver-selenium-python-examples)
782
+ - [JavaScript examples](https://github.com/captcha-solver-api/javascript-examples)
783
+ - [Tencent CAPTCHA automation examples](https://github.com/captcha-solver-api/How-to-Automate-Tencent-CAPTCHA)
784
+ - [Cloudflare Turnstile Puppeteer Demo](https://github.com/captcha-solver-api/cloudflare-turnstile-puppeteer-demo) — a working browser automation example for Cloudflare Turnstile.
785
+ - [How to Automate Tencent CAPTCHA](https://captcha-solver.com/en/blog/how-to-automate-tencent-captcha) — a step-by-step guide with Python and JavaScript SDK examples.
786
+
741
787
  ## License
742
788
 
743
789
  This project is licensed under the MIT License. See [LICENSE.md](https://github.com/captcha-solver-api/python-sdk/blob/main/LICENSE.md) for details.
@@ -6,3 +6,7 @@ pytest>=7
6
6
  pytest-asyncio>=0.21
7
7
  pytest-mock>=3
8
8
  python-dotenv>=1.0
9
+ ruff>=0.6
10
+ mypy>=1.10
11
+ types-requests>=2.28
12
+ pytest-cov>=4
@@ -45,6 +45,10 @@ dev = [
45
45
  "pytest-asyncio>=0.21",
46
46
  "pytest-mock>=3",
47
47
  "python-dotenv>=1.0",
48
+ "ruff>=0.6",
49
+ "mypy>=1.10",
50
+ "types-requests>=2.28",
51
+ "pytest-cov>=4",
48
52
  ]
49
53
 
50
54
  [project.urls]
@@ -63,3 +67,34 @@ captcha_solver_api = ["py.typed"]
63
67
 
64
68
  [tool.pytest.ini_options]
65
69
  asyncio_mode = "auto"
70
+
71
+ [tool.coverage.run]
72
+ source = ["captcha_solver_api"]
73
+ branch = true
74
+
75
+ [tool.coverage.report]
76
+ show_missing = true
77
+
78
+ [tool.mypy]
79
+ # mypy 2.x cannot target 3.9; 3.10 is the lowest it accepts. Runtime
80
+ # compatibility with 3.9 is still covered by the CI test matrix.
81
+ python_version = "3.10"
82
+ strict = true
83
+ files = ["captcha_solver_api"]
84
+
85
+ [tool.ruff]
86
+ target-version = "py39"
87
+ line-length = 100
88
+ extend-exclude = [".pytest_cache", "*.md"]
89
+
90
+ [tool.ruff.lint]
91
+ select = ["E", "F", "W", "I", "UP", "B", "N", "PIE", "RUF"]
92
+ ignore = [
93
+ "UP006", "UP035", "UP045", # keep typing.Optional/Dict/List for 3.9 readability
94
+ "N803", "N815", # task fields mirror API camelCase on purpose
95
+ "E501", # long lines in docstrings/tables
96
+ ]
97
+
98
+ [tool.ruff.lint.per-file-ignores]
99
+ "examples/**" = ["E402"] # imports after sys.path/dotenv setup
100
+ "tests/**" = ["RUF012"]
@@ -5,9 +5,9 @@ from unittest.mock import AsyncMock
5
5
 
6
6
  import pytest
7
7
 
8
- import captcha_solver_api.client as sync_module
9
8
  import captcha_solver_api.async_client as async_module
10
- from captcha_solver_api import CaptchaClient, AsyncCaptchaClient, CaptchaTimeoutError
9
+ import captcha_solver_api.client as sync_module
10
+ from captcha_solver_api import AsyncCaptchaClient, CaptchaClient, CaptchaTimeoutError
11
11
  from captcha_solver_api.tasks import RecaptchaV2TaskProxyless
12
12
 
13
13
 
@@ -30,60 +30,62 @@ CASES = [
30
30
  ]
31
31
 
32
32
 
33
- @pytest.mark.parametrize('interval,timeout,expected_polls,times_out', CASES)
33
+ @pytest.mark.parametrize("interval,timeout,expected_polls,times_out", CASES)
34
34
  def test_sync_polling_schedule(monkeypatch, interval, timeout, expected_polls, times_out):
35
35
  clock = Clock()
36
36
  polls = []
37
- monkeypatch.setattr(sync_module, 'time', clock)
37
+ monkeypatch.setattr(sync_module, "time", clock)
38
38
 
39
39
  def request(endpoint, payload):
40
- if endpoint == 'createTask':
40
+ if endpoint == "createTask":
41
41
  assert clock.now == 0
42
- return {'errorId': 0, 'taskId': 100}
43
- assert payload['taskId'] == 100
42
+ return {"errorId": 0, "taskId": 100}
43
+ assert payload["taskId"] == 100
44
44
  polls.append(clock.now)
45
45
  if len(polls) == 2:
46
- return {'errorId': 0, 'status': 'ready', 'solution': {'token': 'done'}}
47
- return {'errorId': 0, 'status': 'processing'}
46
+ return {"errorId": 0, "status": "ready", "solution": {"token": "done"}}
47
+ return {"errorId": 0, "status": "processing"}
48
48
 
49
- options = {} if interval is None else {'polling_interval': interval}
50
- with CaptchaClient('test-key', **options) as client:
51
- monkeypatch.setattr(client, '_request', request)
52
- task = RecaptchaV2TaskProxyless('https://example.com', 'site-key')
49
+ options = {} if interval is None else {"polling_interval": interval}
50
+ with CaptchaClient("test-key", **options) as client:
51
+ monkeypatch.setattr(client, "_request", request)
52
+ task = RecaptchaV2TaskProxyless("https://example.com", "site-key")
53
53
  if times_out:
54
54
  with pytest.raises(CaptchaTimeoutError):
55
55
  client.solve(task, timeout=timeout)
56
56
  assert clock.now == timeout
57
57
  else:
58
- assert client.solve(task, timeout=timeout) == {'token': 'done'}
58
+ assert client.solve(task, timeout=timeout) == {"token": "done"}
59
59
  assert polls == expected_polls
60
60
 
61
61
 
62
- @pytest.mark.parametrize('interval,timeout,expected_polls,times_out', CASES)
62
+ @pytest.mark.parametrize("interval,timeout,expected_polls,times_out", CASES)
63
63
  async def test_async_polling_schedule(monkeypatch, interval, timeout, expected_polls, times_out):
64
64
  clock = Clock()
65
65
  polls = []
66
- monkeypatch.setattr(async_module, 'time', clock)
67
- monkeypatch.setattr(async_module, 'asyncio', SimpleNamespace(sleep=AsyncMock(side_effect=clock.sleep)))
66
+ monkeypatch.setattr(async_module, "time", clock)
67
+ monkeypatch.setattr(
68
+ async_module, "asyncio", SimpleNamespace(sleep=AsyncMock(side_effect=clock.sleep))
69
+ )
68
70
 
69
71
  async def request(endpoint, payload):
70
- if endpoint == 'createTask':
72
+ if endpoint == "createTask":
71
73
  assert clock.now == 0
72
- return {'errorId': 0, 'taskId': 100}
73
- assert payload['taskId'] == 100
74
+ return {"errorId": 0, "taskId": 100}
75
+ assert payload["taskId"] == 100
74
76
  polls.append(clock.now)
75
77
  if len(polls) == 2:
76
- return {'errorId': 0, 'status': 'ready', 'solution': {'token': 'done'}}
77
- return {'errorId': 0, 'status': 'processing'}
78
+ return {"errorId": 0, "status": "ready", "solution": {"token": "done"}}
79
+ return {"errorId": 0, "status": "processing"}
78
80
 
79
- options = {} if interval is None else {'polling_interval': interval}
80
- async with AsyncCaptchaClient('test-key', **options) as client:
81
- monkeypatch.setattr(client, '_request', request)
82
- task = RecaptchaV2TaskProxyless('https://example.com', 'site-key')
81
+ options = {} if interval is None else {"polling_interval": interval}
82
+ async with AsyncCaptchaClient("test-key", **options) as client:
83
+ monkeypatch.setattr(client, "_request", request)
84
+ task = RecaptchaV2TaskProxyless("https://example.com", "site-key")
83
85
  if times_out:
84
86
  with pytest.raises(CaptchaTimeoutError):
85
87
  await client.solve(task, timeout=timeout)
86
88
  assert clock.now == timeout
87
89
  else:
88
- assert await client.solve(task, timeout=timeout) == {'token': 'done'}
90
+ assert await client.solve(task, timeout=timeout) == {"token": "done"}
89
91
  assert polls == expected_polls
@@ -1 +0,0 @@
1
- __version__ = "1.0.1"