python3-capsolver is a Python 3.8+ client library (SDK) for the Capsolver cloud captcha-solving API. It ships one package, python3_capsolver, distributed on PyPI, with a dual execution model: every capability exists twice — synchronous via requests, asynchronous via aiohttp (src/python3_capsolver/core/sio_captcha_instrument.py, src/python3_capsolver/core/aio_captcha_instrument.py, dependency list in pyproject.toml). The API base URL is a constant (REQUEST_URL in src/python3_capsolver/core/const.py); the library keeps no state beyond per-instance request payloads.
Each supported captcha family is one thin service module (ReCaptcha, Cloudflare, GeeTest, DataDome, MtCaptcha, FriendlyCaptcha, Yandex SmartCaptcha, AWS WAF, image-to-text OCR, AI vision) plus a raw-API escape hatch, control.py; all types are registered in CaptchaTypeEnm (src/python3_capsolver/core/enum.py).
The architecture is a four-layer library with one-way dependencies: thin service classes → shared base class → HTTP instruments → support layer (schemas, enums, constants). There is no server, CLI, database, or background worker. The test suite is a live integration suite against the real Capsolver API, gated on the API_KEY secret (tests/conftest.py, .github/workflows/test.yml). The toolchain is uv (uv.lock, Makefile).
Evidence anchors: pyproject.toml, src/python3_capsolver/core/base.py, src/python3_capsolver/core/const.py, src/python3_capsolver/core/enum.py, src/python3_capsolver/control.py, tests/conftest.py.
No material unknowns: the entire runtime surface is plain importable source with no generated or vendored code.
Service (recaptcha.py, cloudflare.py, control.py, ...)
│ inherits CaptchaParams (control.py also calls instruments directly)
▼
Base (core/base.py — CaptchaParams)
│ instantiates per-call instruments
▼
Instrument (core/sio_captcha_instrument.py, core/aio_captcha_instrument.py,
│ core/captcha_instrument.py)
▼
Support (core/serializer.py, core/enum.py, core/const.py,
core/utils.py, core/context_instr.py)
- Responsibility: define per-captcha-type constructor parameters; expose solving entry points to users.
- Code locations:
src/python3_capsolver/*.py— one module per captcha family. - Entry points: public classes such as
ReCaptcha(src/python3_capsolver/recaptcha.py) andControl(src/python3_capsolver/control.py); the solving methodscaptcha_handler()/aio_captcha_handler()are inherited from the base, not redefined per service. - Depends on: base layer (
core/base.py) and support enums (core/enum.py);control.pyadditionally imports both instruments directly. - Must not depend on:
requests,aiohttp, or any HTTP library. - Owns: captcha-type-specific task parameter names only.
- State and external boundaries: none — pure parameter holders.
- Evidence: import blocks of all
src/python3_capsolver/*.py(none import an HTTP library; onlycontrol.pyimports instruments).
- Responsibility: merge service constructor arguments with per-call
task_payload; dispatch to the sync or async instrument; providewith/async withsupport. - Code locations:
src/python3_capsolver/core/base.py. - Entry points:
CaptchaParams.captcha_handler(),CaptchaParams.aio_captcha_handler(). - Depends on: instrument layer and support layer (
serializer.py,enum.py,const.py,context_instr.py,captcha_instrument.py). - Must not depend on: any service module — nothing in
core/imports from the package root. - Owns: the mutable request payloads (
create_task_payload,get_result_params,task_params) that instruments read. - State and external boundaries: holds user credentials (
clientKey) in memory; performs no I/O of its own. - Evidence:
core/base.pyimports;class CaptchaParams(SIOContextManager, AIOContextManager).
- Responsibility: all network I/O — build requests from msgspec structs, mount retry adapters, run the create-then-poll cycle; prepare image files (base64-encode local files, download from URLs).
- Code locations:
src/python3_capsolver/core/sio_captcha_instrument.py(sync,requests),src/python3_capsolver/core/aio_captcha_instrument.py(async,aiohttp),src/python3_capsolver/core/captcha_instrument.py(CaptchaInstrumentBaseplusFileInstrument). - Entry points:
SIOCaptchaInstrument.processing_captcha(),AIOCaptchaInstrument.processing_captcha(), staticsend_post_request()(the pathcontrol.pyuses),FileInstrument.file_processing()/aio_file_processing()(called directly by consumers, per docstring examples). - Depends on: support layer only (
serializer.py,enum.py,const.py,utils.py). - Must not depend on: base or service modules — instruments receive a
captcha_paramsinstance per call but import nothing from above their layer. - Owns: HTTP sessions, retry-adapter wiring, the polling loop and its attempt budget.
- State and external boundaries: the only code that talks to
https://api.capsolver.com; may read local files and fetch image URLs for file-based tasks. - Evidence: import blocks of the three instrument files;
RETRIESmounted onrequests.Sessioninsio_captcha_instrument.py.
- Responsibility: API request/response schemas, enums, constants, retry-policy objects, context-manager mixins, attempt generator.
- Code locations:
src/python3_capsolver/core/serializer.py,core/enum.py,core/const.py,core/utils.py,core/context_instr.py. - Entry points: not applicable — consumed by upper layers.
- Depends on: stdlib plus
msgspec,tenacity,requests.adapters(retry-policy objects only). - Must not depend on: base, instrument, or service layers.
- Owns:
msgspec.Structrequest/response models withto_dict();CaptchaTypeEnm;REQUEST_URL,RETRIES,ASYNC_RETRIES,VALID_STATUS_CODES. - State and external boundaries: none; disables urllib3 warnings at import time (
const.py). - Evidence: import blocks of the five support modules — all comply.
.
├── src/python3_capsolver/ # the shipped package (setuptools src layout)
│ ├── core/ # base + instruments + support (see §2)
│ │ ├── base.py # CaptchaParams — shared dispatcher
│ │ ├── captcha_instrument.py # CaptchaInstrumentBase + FileInstrument
│ │ ├── sio_captcha_instrument.py # sync HTTP + create-then-poll loop
│ │ ├── aio_captcha_instrument.py # async HTTP + create-then-poll loop
│ │ └── serializer.py, enum.py, const.py, utils.py, context_instr.py
│ ├── control.py # raw API access: get_balance, create_task, get_task_result
│ ├── recaptcha.py, cloudflare.py, gee_test.py, datadome_slider.py,
│ │ mt_captcha.py, friendly_captcha.py, yandex.py, aws_waf.py,
│ │ image_to_text.py, vision_engine.py # one facade per captcha family
│ └── __init__.py # exports __version__ only; full-path imports by design
├── tests/ # live integration suite, one file per service
│ ├── conftest.py # BaseTest (reads API_KEY env), delay fixtures
│ └── files/ # captcha images for file-based tests
├── docs/ # Sphinx sources; per-module pages in docs/modules/
├── files/ # images referenced by README and docs
├── pyproject.toml, uv.lock # build, dependencies, tool config (uv toolchain)
├── Makefile # tests, lint, refactor, build, upload, doc
└── .coveragerc # coverage scoped to python3_capsolver/, tests omitted
Where is X?
- Adding a captcha type → new module in
src/python3_capsolver/inheritingCaptchaParams, plus a value inCaptchaTypeEnm(core/enum.py). - HTTP retry and polling tuning →
core/const.py(RETRIES,ASYNC_RETRIES) and theattempts_generatordefault incore/utils.py. - API payload/response schemas →
core/serializer.py. - The create-then-poll loop →
__create_task/__get_resultin both instrument modules. - Image/file preprocessing (base64, URL fetch) →
FileInstrumentincore/captcha_instrument.py, invoked at the consumer call site before results entertask_payload.
- Trigger: consumer instantiates a facade, e.g.
ReCaptcha(api_key=..., captcha_type=CaptchaTypeEnm.ReCaptchaV2TaskProxyLess), then callscaptcha_handler(task_payload={...})(oraio_captcha_handler()). - Entry point:
CaptchaParams.captcha_handler()insrc/python3_capsolver/core/base.py. - Coordination:
CaptchaParamsmergestask_payloadintoself.task_paramsand instantiatesSIOCaptchaInstrument(self)(AIOCaptchaInstrumentfor async). - Core or domain processing:
processing_captcha()builds aRequestCreateTaskSerstruct and POSTs to/createTask; if the task is not immediately ready, it sleepssleep_timeand polls/getTaskResultup to 15 times (attempts_generator()yields 1..15), returning on statusreadyorfailed, or a synthesized failure response on exhaustion. - Persistence or external interaction: the only external system is the Capsolver API at
REQUEST_URL(core/const.py); there is no local persistence. - Output or side effect: a
dictwith the full API response, including the solution.
Architectural boundaries crossed:
- Service → Base → Instrument → Support; the result crosses back up as plain dicts.
Evidence:
src/python3_capsolver/core/base.pysrc/python3_capsolver/core/sio_captcha_instrument.pysrc/python3_capsolver/core/aio_captcha_instrument.pysrc/python3_capsolver/core/utils.py
- Trigger: consumer calls e.g.
Control(api_key=...).get_balance()oraio_get_balance(). - Entry point:
Controlmethods insrc/python3_capsolver/control.py. - Coordination: none —
Controlskips the payload-merge and polling abstraction and invokes instruments directly. - Core or domain processing: one-shot
SIOCaptchaInstrument.send_post_request()/AIOCaptchaInstrument.send_post_request()targeting anEndpointPostfixEnmendpoint;create_task()/get_task_result()accept hand-built task payloads without the solving-loop "sugar". - Persistence or external interaction: same single Capsolver API boundary.
- Output or side effect: raw API response
dict.
Architectural boundaries crossed:
- Service → Instrument directly — the one sanctioned exception to "services never touch instruments".
Evidence:
src/python3_capsolver/control.py
- Rule: dependency direction is one-way — Service → Base → Instrument → Support.
- Rationale: keeps HTTP details out of facades and keeps the support layer reusable without cycles.
- Enforcement / Signals: convention, verified by import inspection; no import-linter or architecture test exists. CI lint (
.github/workflows/lint.yml,make lint) checks formatting only (autoflake, black, isort).
- Rule: service modules must not import
requestsoraiohttp.- Rationale: all HTTP lives in the instrument layer.
- Enforcement / Signals: convention plus review; the import block of every service module complies today.
- Rule: every capability ships in both sync and async form.
- Rationale: dual-mode operation is the library's core offering (
Framework :: AsyncIOclassifier inpyproject.toml). - Enforcement / Signals: paired classes and methods (
SIOCaptchaInstrument/AIOCaptchaInstrument,file_processing/aio_file_processing,SIOContextManager/AIOContextManager) and paired tests (tests/test_sio_captcha_instrument.py,tests/test_aio_captcha_instrument.py).
- Rationale: dual-mode operation is the library's core offering (
- Rule: all API serialization uses
msgspec.Structsubclasses withto_dict(); thejsonmodule is never imported insrc/.- Rationale: a single typed serialization path for all API contracts.
- Enforcement / Signals:
core/serializer.pyis the only schema home; nojsonimport exists anywhere insrc/.
- Rule: new captcha types must be registered in
CaptchaTypeEnm.- Rationale:
CaptchaParams.__init__buildsTaskSer(type=captcha_type.value)from the enum, so dispatch depends on it. - Enforcement / Signals: runtime failure for unregistered types;
core/base.pyconstructor; change rules insrc/python3_capsolver/AGENTS.md.
- Rationale:
- Rule: retry and polling budgets are centralized in
core/const.pyandcore/utils.py, not per-instrument literals.- Rationale: a single point of change covering both execution modes.
- Enforcement / Signals:
RETRIES,ASYNC_RETRIES,VALID_STATUS_CODESinconst.py; consumed bysio_captcha_instrument.pyandcaptcha_instrument.py;attempts_generatorinutils.pydrives the poll loop.
- Rule: package
__init__.pyfiles stay minimal (root exports only__version__); users import via full module paths.- Rationale: explicit imports with no facade namespace or re-export cycles.
- Enforcement / Signals:
src/python3_capsolver/__init__.pyandcore/__init__.pycontents; all docstring examples use full-path imports.
- Rule: source must remain Python 3.8-compatible.
- Rationale: the declared support floor.
- Enforcement / Signals:
requires-python = ">=3.8"and 3.8–3.14 classifiers inpyproject.toml; 3.8–3.14 matrix in.github/workflows/build.ymlandinstall.yml.
- Rule: tests are live integration tests gated on
API_KEY, with rate-limiting delays.- Rationale: the suite exercises the real Capsolver API; delays prevent throttling.
- Enforcement / Signals:
tests/conftest.py(BaseTest.API_KEY = os.environ["API_KEY"],delay_func1 s per function,delay_class2 s per class);.github/workflows/test.ymlinjects the secret and runsmake testson Python 3.12.
- Rule: repo-only instruction documents must not ship in the wheel.
- Rationale:
AGENTS.mdfiles are contributor tooling, not user documentation. - Enforcement / Signals:
MANIFEST.inincludes onlyREADME.mdandLICENSE.
- Rationale:
ARCHITECTURE.md (this file) owns the global architecture map: layers, dependency direction, code map, representative flows, and invariants.
AGENTS.md(root) — repository-wide operating rules, validation commands, and context routing to the child files below.src/python3_capsolver/AGENTS.md— service-layer conventions and per-service change rules.src/python3_capsolver/core/AGENTS.md— core module internals and safe-change guidance.tests/AGENTS.md— test patterns, fixtures, and how to add tests for new services.README.md(andREADME.es-ES.md) — user-facing usage and installation.docs/— Sphinx API reference built withmake doc(CI:.github/workflows/build_sphinx.yml); one page per module underdocs/modules/.
No ADRs, runbooks, or DESIGN.md exist in the repository; for a single-package library of this size their absence does not limit architectural understanding.