a13n
Build AI agents and run them on cloud.
Captcha solving API reference: request and response shapes, challenge types, proxy format, polling, balance and error codes. Billed only on successful solves.
Translation pending
Developer toolsPythonGitHub @DishantSinghDev2OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Official CaptchaNo Python SDK: solve reCAPTCHA v2/v3/Enterprise, Cloudflare Turnstile, DataDome and image captchas. Sync + async, fully typed, httpx.
Translation pending
Loading the full README.
Read the full README ↗The official Python SDK for CaptchaNo: solve reCAPTCHA v2, v3 and Enterprise, Cloudflare Turnstile, DataDome and image captchas from Python, with a fully typed sync and async client using httpx.
Keywords: captcha solver · captcha solving API · reCAPTCHA solver · Turnstile solver · DataDome · image captcha · web scraping · Playwright · Selenium
CaptchaNo) and async (AsyncCaptchaNo) clients with identical APIs?wait= blocking mode, then polls only if neededrecaptcha_v2, recaptcha_v3, recaptcha_enterprise, turnstile, datadome, imageInsufficientBalanceError, InvalidSitekeyError, …)429 / 503; a solve request is never re-sent after a task was createdhttpx), ships py.typed, passes mypy --strictpip install captchano
# or: uv add captchano · poetry add captchano
Python 3.9+.
Create an API key in the dashboard and export it:
export CAPTCHANO_API_KEY="cn-..."
from captchano import CaptchaNo
client = CaptchaNo() # reads CAPTCHANO_API_KEY
task = client.recaptcha_v2("https://example.com/login", sitekey="6Lc...")
print(task.token) # submit this as g-recaptcha-response
print(task.cost) # USD charged for this solve, e.g. "0.00598"
solve() and every helper return a Task whose status is "ready", or raise a typed exception. You are only billed for solves that return a usable solution.
Async:
import asyncio
from captchano import AsyncCaptchaNo
async def main() -> None:
async with AsyncCaptchaNo() as client:
task = await client.turnstile("https://example.com", sitekey="0x4AAAAAAA...")
print(task.token)
asyncio.run(main())
from captchano import CaptchaNo
client = CaptchaNo()
# reCAPTCHA v2 (checkbox or invisible)
task = client.recaptcha_v2("https://example.com/login", "6Lc...", invisible=False)
task.token
# reCAPTCHA v3 (score-based): send the action the page reports
task = client.recaptcha_v3("https://example.com/login", "6Lc...", action="login", min_score=0.7)
task.token
# reCAPTCHA Enterprise (checkbox-style widgets)
task = client.recaptcha_enterprise("https://example.com/login", "6Lc...")
task.token
# Cloudflare Turnstile (action / c_data / page_data are optional)
task = client.turnstile("https://example.com", "0x4AAAAAAA...", action="login")
task.token
# DataDome: proxy is required; replay the returned user agent
task = client.datadome(
"https://example.com/",
challenge_url="https://geo.captcha-delivery.com/captcha/?initialCid=...",
proxy="http://user:pass@host:port",
user_agent="Mozilla/5.0 ...",
)
task.cookie, task.user_agent
# Image / text captcha: raw bytes or a base64 string
with open("captcha.png", "rb") as f:
task = client.image(f.read())
task.text
Or call solve() directly with any type:
task = client.solve(
"recaptcha_v2",
url="https://example.com/login",
sitekey="6Lc...",
proxy="http://user:pass@host:port", # or {"scheme": "http", "host": ..., "port": ..., "username": ..., "password": ...}
user_agent="Mozilla/5.0 ...",
wait=60, # seconds the API holds the request open (max 120)
timeout=180, # total seconds before SolveTimeoutError
)
Passing the proxy that will submit the token is the single biggest lever on acceptance: a token minted behind a different IP is the most common reason a valid-looking token is rejected. When a solution includes user_agent, replay it exactly.
task = client.submit("turnstile", "https://example.com", "0x4AAAAAAA...") # returns at once, status "solving"
# ... do other work ...
task = client.wait_for(task.id, timeout=120) # polls GET /v1/task/{id}
# or a single look-up that never raises on a failed task:
task = client.get_task(task.id)
Solutions stay retrievable for 10 minutes.
from playwright.sync_api import sync_playwright
from captchano import CaptchaNo
client = CaptchaNo()
URL = "https://example.com/login"
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto(URL)
sitekey = page.get_attribute("[data-sitekey]", "data-sitekey")
task = client.recaptcha_v2(URL, sitekey)
page.evaluate(
"""token => {
document.querySelectorAll('[name="g-recaptcha-response"]').forEach(el => { el.value = token; el.innerHTML = token; });
}""",
task.token,
)
page.click("button[type=submit]")
If the site uses a JavaScript callback instead of a form post, call it with the token (the name is in the widget's data-callback attribute).
sitekey = page.get_attribute(".cf-turnstile", "data-sitekey")
task = client.turnstile(page.url, sitekey)
page.evaluate(
"token => document.querySelectorAll('[name=\"cf-turnstile-response\"]').forEach(el => el.value = token)",
task.token,
)
from selenium import webdriver
from selenium.webdriver.common.by import By
from captchano import CaptchaNo
client = CaptchaNo()
driver = webdriver.Chrome()
driver.get("https://example.com/login")
sitekey = driver.find_element(By.CSS_SELECTOR, "[data-sitekey]").get_attribute("data-sitekey")
task = client.recaptcha_v2(driver.current_url, sitekey)
driver.execute_script(
"document.querySelectorAll('[name=\"g-recaptcha-response\"]').forEach(el => { el.style.display = 'block'; el.value = arguments[0]; });",
task.token,
)
driver.find_element(By.CSS_SELECTOR, "button[type=submit]").click()
import httpx
proxy = "http://user:pass@host:port"
task = client.datadome(url, challenge_url=challenge_url, proxy=proxy, user_agent=ua)
name, _, value = task.cookie.split(";", 1)[0].partition("=")
with httpx.Client(proxy=proxy, headers={"User-Agent": task.user_agent or ua}) as http:
http.cookies.set(name, value, domain=".example.com")
print(http.get(url).status_code)
Use the same proxy and user agent for the follow-up requests, and keep the cookie in your session instead of solving again on every request.
from captchano import (
CaptchaNoError, AuthenticationError, InsufficientBalanceError, InvalidSitekeyError,
UnsolvableError, RateLimitError, SolveTimeoutError,
)
try:
task = client.recaptcha_v2(url, sitekey)
except InsufficientBalanceError:
print("Top up at https://captchano.com/dashboard")
except InvalidSitekeyError as e:
print(f"Check the sitekey: {e.message}")
except UnsolvableError:
print("Could not be solved (not billed). Check url/sitekey and retry once.")
except SolveTimeoutError as e:
task = client.wait_for(e.task_id) if e.task_id else None # resume instead of paying twice
except AuthenticationError:
print("Check CAPTCHANO_API_KEY")
except CaptchaNoError as e:
print(e.code, e.status, e.message)
| API code | Exception |
|---|---|
invalid_api_key | AuthenticationError |
insufficient_balance | InsufficientBalanceError |
unsupported_type | UnsupportedTypeError (an InvalidRequestError) |
invalid_sitekey | InvalidSitekeyError (an InvalidRequestError) |
proxy_required | ProxyRequiredError (a ProxyError) |
proxy_unreachable | ProxyUnreachableError (a ProxyError) |
unsolvable | UnsolvableError |
score_not_met | ScoreNotMetError (an UnsolvableError) |
upstream_error | SolveFailedError |
capacity_unavailable | CapacityUnavailableError |
rate_limited | RateLimitError |
task_not_found | TaskNotFoundError |
| — network failure | APIConnectionError (code="network_error") |
| — time limit | SolveTimeoutError (code="timeout") |
Every exception extends CaptchaNoError and carries .code, .message, .status, .task_id, .task and the parsed .body.
CaptchaNo(
api_key="cn-...", # default: $CAPTCHANO_API_KEY
base_url="https://api.captchano.com", # default: $CAPTCHANO_API_BASE or this
timeout=30.0, # per-request network timeout; ?wait= is added on top
poll_interval=5.0, # seconds between task polls
max_retries=2, # 429 / 503 answers, and network errors on reads
http_client=my_httpx_client, # bring your own httpx.Client
)
Never hard-code your key in source files; read it from the environment or a secrets manager.
| Endpoint | Call |
|---|---|
POST /v1/solve?wait=N | client.solve(...) · client.submit(...) · per-type helpers |
GET /v1/task/{id} | client.get_task(id) · client.wait_for(id, timeout) |
GET /v1/balance | client.balance() |
Full reference: https://captchano.com/captcha-solver-api · Prices per successful solve: https://captchano.com/pricing
python -m venv .venv && . .venv/bin/activate
pip install -e ".[dev]"
ruff check src tests && mypy src && pytest
The tests use respx and httpx.MockTransport; they never touch the network.
Issues and pull requests are welcome — see CONTRIBUTING.md. Security reports: SECURITY.md.
MIT © DishIs Technologies LLC