Universal Python interface to multiple anti-captcha services.
Status: highly experimental. Pre-1.0. No public API stability is promised, even for the documented surface. Anything may change between releases.
Documentation: https://lorien.github.io/unicaptcha/
| Provider | Kind string | Default base URL |
|---|---|---|
| 2Captcha (modern JSON API) | twocaptcha |
https://api.2captcha.com |
| Anti-Captcha | anti-captcha |
https://api.anti-captcha.com |
| CapMonster Cloud | capmonster |
https://api.capmonster.cloud |
| Capsolver | capsolver |
https://api.capsolver.com |
2Captcha-protocol mirrors such as RuCaptcha work by overriding the base URL
(RuCaptcha's JSON API v2 is verified complete): TwoCaptchaClient(api_key=..., base_url="https://api.rucaptcha.com"). Smaller mirrors need per-service
verification of their JSON API.
| Kind | Challenge base | Solution base |
|---|---|---|
| Image CAPTCHA | ImageChallenge |
ImageSolution |
| Text CAPTCHA | TextChallenge |
TextSolution |
| reCAPTCHA v2 | RecaptchaV2Challenge |
RecaptchaV2Solution |
| reCAPTCHA v3 | RecaptchaV3Challenge |
RecaptchaV3Solution |
| hCaptcha | HCaptchaChallenge |
HCaptchaSolution |
| FunCaptcha / Arkose | FunCaptchaChallenge |
FunCaptchaSolution |
| GeeTest v3 | GeeTestV3Challenge |
GeeTestV3Solution |
| GeeTest v4 | GeeTestV4Challenge |
GeeTestV4Solution |
| Cloudflare Turnstile | TurnstileChallenge |
TurnstileSolution |
Kind coverage varies by provider (e.g. text is 2Captcha + Anti-Captcha
only); unsupported kinds raise UnsupportedChallengeError.
uv add unicaptcha
Requires Python 3.11+. Single runtime dependency: httpx.
The universal client registers one or more provider adapters and dispatches each challenge to the adapter that supports it.
from pathlib import Path
from unicaptcha import Solver, ImageChallenge
from unicaptcha.provider.twocaptcha import TwoCaptchaAdapter
client = Solver(adapters=[TwoCaptchaAdapter("YOUR_API_KEY")])
result = client.solve(ImageChallenge(Path("captcha.png")))
print(result.solution.text) # the solved captcha textA challenge is a frozen dataclass describing what to solve. The kind-base
classes (ImageChallenge, RecaptchaV2Challenge, …) carry only the fields
every provider shares; the concrete provider classes
(TwoCaptchaImageChallenge, …) add provider-specific options.
- A kind-base challenge routes to any registered adapter supporting the
kind — pass
provider="twocaptcha"to pin one, or omit it for a uniform random pick among the supporting adapters. - Provider extras use the concrete class, e.g.
TwoCaptchaImageChallenge(body, numeric=True, min_len=4). - Image bodies accept
bytesor aPath; the value is normalized to bytes at construction.
Ask which registered adapters cover a challenge kind — no probing, no network:
client.supports(HCaptchaChallenge) # bool
client.providers_supporting(TurnstileChallenge) # ("twocaptcha", ...)
client.supported_kinds() # ("image", "text", ...)kind is a kind-base class (RecaptchaV2Challenge) or its tag string
("recaptcha-v2"); KIND_TAGS / TAG_KINDS map between them. These
methods are synchronous on both Solver and AsyncSolver.
Point the library at a page's HTML and it detects and solves the captcha for you:
from unicaptcha import Solver, detect
from unicaptcha.provider.twocaptcha import TwoCaptchaAdapter
html = open("signup.html").read()
for found in detect(html, "https://example.com/signup"):
print(found.kind, found.signals) # what the page uses
with Solver([TwoCaptchaAdapter("YOUR_API_KEY")]) as client:
auto = client.auto_solve(html, "https://example.com/signup")
token = auto.fill["#g-recaptcha-response"] # inject into the live pageauto_solve solves the first detected captcha and returns an
AutoSolveResult whose fill maps DOM selectors to the solved values.
See the auto-solve guide.
For a single provider, a facade client offers one convenience method per kind with full parameter parity:
import asyncio
from unicaptcha.provider.twocaptcha import AsyncTwoCaptchaClient, TwoCaptchaClient
with TwoCaptchaClient("YOUR_API_KEY") as client:
result = client.solve_image(b"captcha.png", numeric=1)
async def main() -> None:
async with AsyncTwoCaptchaClient("YOUR_API_KEY") as client:
result = await client.solve_recaptcha_v2(
sitekey="SITEKEY", pageurl="https://example.com"
)
print(result.solution.token)
asyncio.run(main())Each provider package exports <Provider>Client and Async<Provider>Client
(TwoCaptchaClient, AntiCaptchaClient, CapMonsterClient,
CapsolverClient, and their async counterparts). Facade constructors take
api_key positionally and otherwise mirror Solver minus adapters
(base_url, referral, currency, proxy, time, retry, network, on_event).
submit() splits solving from collecting: it returns a TaskTicket, which
you can persist (via its task_ref) and collect later.
from unicaptcha import ImageChallenge, TaskRef
ticket = client.submit(ImageChallenge(Path("a.png"))) # collect later
# ... later ...
result = client.wait(ticket) # -> TaskResult, typed
status = client.wait_ref(TaskRef("twocaptcha", 12345), timeout=120) # from a persisted idThe universal client and the facades share the same aux names:
get_balance(provider)→Moneybalance in the provider's currency (facades take no argument)get_task_status(task)→ one-shot status (TaskRef, orinton facades)report_bad_result(task)/report_good_result(task)→bool(coverage varies by provider)get_abandoned_tasks()→ tasks cancelled while solving
Every library exception derives from UnicaptchaError and carries a
kind: ErrorKind plus the verbatim provider response in raw_response:
NoSolutionError, TaskTimeoutError, RateLimitError, ServiceBusyError,
InsufficientBalanceError, AuthenticationError, NetworkError,
ProviderError, InvalidConfigError, InvalidChallengeError,
UnsupportedChallengeError, ClientClosedError.
Pass on_event= at construction or per call to observe the task lifecycle
(SUBMIT_REQUESTED, SUBMIT_ACCEPTED, RESULT_RECEIVED, …) as typed
TaskEvents.
Runnable scripts, one per use case in examples/sync/ and examples/async/:
every captcha kind, two-phase batch, aux ops, events, errors, proxy,
auto-solve. See examples/README.md.
unicaptcha ships a public adapter SDK: third parties can implement their own
provider adapters (BaseAdapter) and register them in the universal client.
The test suite includes a reference third-party adapter, MyServiceAdapter
(tests/_myservice.py), written against the public API only — the pattern to
follow. See spec/docs/architecture.md ("Adapter SDK") for the contract.
MIT. Repository: https://github.com/lorien/unicaptcha