Call the hCaptcha API with the standard-library Python backend
Use the urllib SDK backend without an optional HTTP dependency, and understand its synchronous and threaded async behavior.
Keep the dependency footprint small
The default easycaptcha-sdk installation includes a urllib transport based on Python’s standard library. It is a useful starting point for a command-line tool, a small worker, or an environment where introducing another HTTP package adds maintenance work.
Import UrllibClient from easycaptcha.backends.urllib. The main package exports common types and errors; it does not construct a requests, httpx, or curl_cffi client behind your back. Transport choice remains visible at the import and construction boundary.
Use typed methods instead of rebuilding requests
import os
from easycaptcha.backends.urllib import UrllibClient
with UrllibClient(os.environ["EASYCAPTCHA_API_KEY"]) as client:
balance = client.get_balance()
dollars = int(balance["balance_microcents"]) / 100_000_000
print(f"Available service credit: ${dollars:.2f}")
Formatting a display amount as a decimal is different from performing accounting. Retain the original integer microcents for budget calculations; rounding a displayed value can discard credit that still matters when many small tasks are submitted.
The same client exposes creation, lookup, and feedback. Use the complete token example to connect those methods. Keep the task ID when a request returns pending work, and handle failed snapshots independently of HTTP errors.
Know what the async wrapper does
AsyncUrllibClient moves blocking HTTP into a thread and serializes requests per client. Canceling a caller still waits for the in-flight blocking request to finish before the session is released. This behavior avoids racing session cleanup, but it is not the same as native async networking.
For a high-concurrency async application, compare the execution model with AsyncHttpxClient instead of choosing solely by the number of dependencies. Bound admission at the application level either way. More local coroutines do not increase server concurrency limits.
Use the simplest transport that meets your runtime requirements, and test timeout and shutdown behavior. See sync versus async clients for the scheduling decision and native async Python for a polling loop that does not block the event loop.