尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Official CaptchaNo Python SDK: solve reCAPTCHA v2/v3/Enterprise, Cloudflare Turnstile, DataDome and image captchas. Sync + async, fully typed, httpx.
翻译准备中
正在加载完整 README。
查看完整 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