Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
180ce7b
docs: link package metadata to SDK readme
dariyaanis Oct 28, 2026
8937693
fix: validate required task parameters
dariyaanis Oct 28, 2026
cecf35f
docs: clarify unavailable proxy task variants
dariyaanis Oct 28, 2026
bb4af82
docs: explain py.typed marker
dariyaanis Oct 28, 2026
6b5a3fb
docs: split proxy examples into separate files
dariyaanis Oct 28, 2026
b36877c
docs: add security policy
dariyaanis Oct 28, 2026
413a2ea
chore: set version to 1.0.5
dariyaanis Oct 28, 2026
edaab93
fix: format async proxyless examples
dariyaanis Oct 28, 2026
bda2216
ci: run Python matrix on Ubuntu 22.04
dariyaanis Oct 28, 2026
ef6134c
fix: tighten validation and document required task parameters
dzmitry-duboyski Oct 28, 2026
e16259a
fix: validate version-specific GeeTest parameters
dariyaanis Oct 29, 2026
949f704
Merge remote-tracking branch 'origin/main' into sdk-review-updates
dzmitry-duboyski Nov 3, 2026
c14ef5a
docs: align task docs and examples after merging main
dzmitry-duboyski Nov 3, 2026
b6d324e
docs: close the async client in every async example
dzmitry-duboyski Nov 3, 2026
b7927e9
docs: expand async Image to Text example
dzmitry-duboyski Nov 3, 2026
f37a113
docs: list https proxy type for Yandex SmartCaptcha
dzmitry-duboyski Nov 3, 2026
ae7e970
docs: replace em dashes in contributor docs
dzmitry-duboyski Nov 3, 2026
47c604c
feat: retry transient polling failures in solve()
dzmitry-duboyski Nov 3, 2026
7f81a4d
feat: expose task_id on errors raised after task creation
dzmitry-duboyski Nov 3, 2026
3ea01ba
ci: add dependency audit, bandit, and CodeQL checks
dzmitry-duboyski Nov 3, 2026
abb3e47
ci: configure Dependabot for pip and GitHub Actions
dzmitry-duboyski Nov 3, 2026
8334e2d
build: raise minimum requests and httpx versions
dzmitry-duboyski Nov 3, 2026
423df2d
ci: drop CodeQL job in favor of default setup
dzmitry-duboyski Nov 3, 2026
f3738dd
chore: set version to 1.1.0
dzmitry-duboyski Nov 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Dependabot opens pull requests that update dependencies. Security updates
# for vulnerable dependencies also require "Dependabot alerts" and
# "Dependabot security updates" to be enabled in the repository settings.
version: 2
updates:
# Runtime and dev dependencies declared in pyproject.toml.
- package-ecosystem: pip
directory: /
schedule:
interval: weekly
commit-message:
prefix: build

# Versions of the actions used in .github/workflows.
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
commit-message:
prefix: ci
56 changes: 56 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Security

on:
push:
branches: [main]
pull_request:
# New advisories are published without any change in this repository,
# so re-check the dependencies weekly as well (Mondays, 06:00 UTC).
schedule:
- cron: "0 6 * * 1"
workflow_dispatch:

permissions:
contents: read

# CodeQL is not run here: the repository uses CodeQL default setup
# (Settings -> Code security), and GitHub rejects results from a workflow
# while default setup is enabled.

jobs:
# Known vulnerabilities in the runtime dependencies that users install
# with the package. pip-audit resolves them from pyproject.toml in an
# isolated environment, so the result matches a fresh `pip install`.
dependencies:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install pip-audit
run: python -m pip install pip-audit

- name: Audit runtime dependencies
run: pip-audit .

# Static analysis of the SDK source for insecure patterns, such as disabled
# TLS verification, requests without timeouts, or eval/exec.
bandit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install bandit
run: python -m pip install bandit

- name: Scan package source
run: bandit -r captcha_solver_api
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ Python SDK for the Captcha Solver API, published on PyPI as `captcha-solver-api`

## Project layout

- `captcha_solver_api/` — package: `client.py` (sync), `async_client.py` (async), `tasks.py`, `exceptions.py`
- `tests/sync/`, `tests/async/` — unit tests, HTTP is mocked
- `tests/integration/` — real HTTP requests to a local stub server (`api_server` fixture)
- `examples/sync/`, `examples/async/` — runnable examples
- `DEVELOPMENT.md` — setup, tests, release process
- `captcha_solver_api/` - package: `client.py` (sync), `async_client.py` (async), `tasks.py`, `exceptions.py`
- `tests/sync/`, `tests/async/` - unit tests, HTTP is mocked
- `tests/integration/` - real HTTP requests to a local stub server (`api_server` fixture)
- `examples/sync/`, `examples/async/` - runnable examples
- `DEVELOPMENT.md` - setup, tests, release process

## Commands

Expand Down
4 changes: 2 additions & 2 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ python -m pytest -k turnstile # filter by name

No API key or internet access is needed:

- `tests/sync/`, `tests/async/` — unit tests, HTTP calls are mocked.
- `tests/integration/` — the client sends real HTTP requests to a server started on
- `tests/sync/`, `tests/async/` - unit tests, HTTP calls are mocked.
- `tests/integration/` - the client sends real HTTP requests to a server started on
`127.0.0.1` by the `api_server` fixture, which records path, headers and body.
Use it to check what actually goes over the wire (e.g. the `X-SDK` header).

Expand Down
62 changes: 48 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ enabled in your editor, hover over `CaptchaClient`, `AsyncCaptchaClient`, their
methods, or a task class such as `RecaptchaV2TaskProxyless` to read the documentation.
Signature help shows argument names, types, and defaults while you type a call.

The `captcha_solver_api/py.typed` file is intentionally empty. It is the standard
[PEP 561](https://peps.python.org/pep-0561/) marker that tells type checkers to use
the inline type annotations shipped with this package.

Docstrings describe parameters, solution fields, exceptions, and usage examples.
Task examples assume an existing synchronous `client`; with `AsyncCaptchaClient`,
use `solution = await client.solve(task)` instead. Async snippets run inside an
Expand Down Expand Up @@ -151,15 +155,19 @@ 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) | ✅ | ❌ |
| Yandex SmartCaptcha Image | ✅ | - |
| Coordinates (click captcha) | ✅ | - |
| Tencent | ✅ | ✅ |

A dash (`-`) means that this CAPTCHA type has no separate task class that accepts
your own proxy; it does not indicate that solving the CAPTCHA is unsupported.

## Client Reference

Every method below is available on both `CaptchaClient` (sync, `requests`-based) and
Expand Down Expand Up @@ -207,14 +215,17 @@ solution -- wraps `create_task()` + `get_task_result()` so you don't poll by han

Returns the `solution` dict once `status` is `"ready"` -- its shape depends on
the task type (see [Captcha Types](#captcha-types)).
Raises `ApiError`, `CaptchaTimeoutError`, or `NetworkError`.
Raises `ValidationError` before any request if `task` is not a task object or a
required task parameter is missing or empty. Otherwise raises `ApiError`,
`CaptchaTimeoutError`, or `NetworkError`.

### `create_task(task, language_pool=None)`

Submits `task` and returns its numeric task ID without waiting for a solution.
Accepts `task` and `language_pool`; it does not accept `timeout`. Use this instead of `solve()` if you need to
manage polling yourself (e.g. checking on many tasks from a different process).
Raises `ApiError`, `CaptchaTimeoutError`, or `NetworkError`.
Raises `ValidationError` before any request if `task` is invalid (same rules as
`solve()`). Otherwise raises `ApiError`, `CaptchaTimeoutError`, or `NetworkError`.

### `get_task_result(task_id)`

Expand Down Expand Up @@ -479,7 +490,7 @@ print(result["validate"])
print(result["seccode"])
```
`challenge` is session-specific and expires quickly, so it can't be hardcoded
into a static example -- see [examples/sync/geetest_v3.py](https://github.com/captcha-solver-api/python-sdk/blob/main/examples/sync/geetest_v3.py)
into a static example -- see [examples/sync/geetest_v3_proxyless.py](https://github.com/captcha-solver-api/python-sdk/blob/main/examples/sync/geetest_v3_proxyless.py)
for where the fetch belongs in the flow.

```python
Expand Down Expand Up @@ -662,6 +673,16 @@ task lifetime. A local timeout does not cancel a task or mean that the API
failed to solve it. Use `create_task()` and keep its `task_id` if you need to
check the same task later; calling `solve()` again creates a new task.

While polling, `solve()` retries short network failures and HTTP timeouts of
`getTaskResult` on the next poll, so a brief outage does not lose an already
created task. After `poll_retries` (default 3) consecutive failures the error is
raised. API errors are not retried, and `createTask` is never retried because
that could create and pay for a second task. To change the limit:

```python
client.poll_retries = 5
```

### Worker language pool

Set a default `language_pool` once at construction instead of passing it to every call:
Expand Down Expand Up @@ -731,13 +752,26 @@ try:
except ValidationError as e:
print(f"Invalid argument: {e}")
except ApiError as e:
print(f"API error: {e.error_code} {e.error_description}")
except CaptchaTimeoutError:
print("Task timed out")
print(f"API error: {e.error_code} {e.error_description} (task {e.task_id})")
except CaptchaTimeoutError as e:
print(f"Task {e.task_id} timed out")
except NetworkError as e:
print(f"Network error: {e}")
```

Every SDK error has a `task_id` attribute. It is set when the error happens
after the task was created (while polling in `solve()` or in `get_task_result()`)
and is `None` otherwise. A polling timeout does not cancel the task, so you can
keep checking it:

```python
try:
result = client.solve(task)
except CaptchaTimeoutError as e:
if e.task_id is not None:
status = client.get_task_result(e.task_id)
```

`TimeoutError` is still exported as a deprecated alias of `CaptchaTimeoutError`.
Avoid importing it by name: it shadows Python's built-in `TimeoutError`.

Expand All @@ -751,12 +785,12 @@ sync/async example list, setup steps, expected results, and placeholder guidance
`CAPTCHA_API_KEY` -- they read sample images bundled in
[examples/assets](https://github.com/captcha-solver-api/python-sdk/tree/main/examples/assets), no target page needed.
`python-dotenv` is optional: install it only if you want the scripts to load a `.env` file.
- **Token captchas** (`recaptcha_v2.py`, `recaptcha_v2_enterprise.py`, `recaptcha_v3.py`,
`turnstile.py`, `yandex_smartcaptcha.py`, `geetest_v4.py`, `tencent.py`) use
- **Token captchas** (`recaptcha_v2_*.py`, `recaptcha_v2_enterprise_*.py`, `recaptcha_v3.py`,
`turnstile_*.py`, `yandex_smartcaptcha_*.py`, `geetest_v4_*.py`, `tencent_*.py`) use
placeholder values (`https://example.com/...`, `YOUR_WEBSITE_KEY`, `YOUR_APP_ID`,
`YOUR_CAPTCHA_ID`) -- replace these with the real values from your own target
page before running.
- **`geetest_v3.py`** additionally needs `challenge` fetched fresh for every
- **`geetest_v3_*.py`** additionally needs `challenge` fetched fresh for every
request -- it's single-use and expires within seconds, so it can't be
hardcoded into a static example. `"https://target-site.com/path/to/geetest/init"`
is a placeholder; replace it with a request to your own target's equivalent
Expand Down Expand Up @@ -797,8 +831,8 @@ Full API reference: https://captcha-solver.com/en/docs/captcha-types
- [Selenium Python examples](https://github.com/captcha-solver-api/captcha-solver-selenium-python-examples)
- [JavaScript examples](https://github.com/captcha-solver-api/javascript-examples)
- [Tencent CAPTCHA automation examples](https://github.com/captcha-solver-api/How-to-Automate-Tencent-CAPTCHA)
- [Cloudflare Turnstile Puppeteer Demo](https://github.com/captcha-solver-api/cloudflare-turnstile-puppeteer-demo) — a working browser automation example for Cloudflare Turnstile.
- [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.
- [Cloudflare Turnstile Puppeteer Demo](https://github.com/captcha-solver-api/cloudflare-turnstile-puppeteer-demo) - a working browser automation example for Cloudflare Turnstile.
- [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.

## License

Expand Down
36 changes: 36 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Security Policy

## Supported versions

Security fixes are provided for the latest released version of
`captcha-solver-api`. Before reporting a vulnerability, verify that it is still
present after upgrading to the latest version available on PyPI.

| Version | Supported |
|---|---|
| Latest release | Yes |
| Older releases | No |

## Reporting a vulnerability

Please report suspected vulnerabilities privately through
[GitHub Security Advisories](https://github.com/captcha-solver-api/python-sdk/security/advisories/new).
Do not open a public issue or pull request for an undisclosed vulnerability.

Include the following information when possible:

- the affected SDK version and Python version;
- a clear description of the impact;
- steps or a minimal example that reproduces the problem;
- any known mitigations or suggested fixes.

You should receive an acknowledgement within five business days. We will keep
you informed while the report is investigated and will coordinate disclosure
after a fix is available. Please allow a reasonable amount of time for the
issue to be addressed before making it public.

## Scope

This policy covers the Python SDK in this repository. Vulnerabilities in the
Captcha Solver service or website should be reported through the security
channel of the corresponding project.
2 changes: 1 addition & 1 deletion captcha_solver_api/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.0.4"
__version__ = "1.1.0"
Loading
Loading