# Python

> YesCaptcha Python samples. Check the balance, read an image captcha, a reusable client class (create a task, poll every 5 seconds), asyncio concurrency control, and submitting reCAPTCHA with requests and Selenium.

Source: https://docs.yescaptcha.com/en/samples/python/ · Last verified: 2026-10-15

Requirements: Python 3.8 or later and `pip install requests`. Set the environment variable before running:

```bash
export YESCAPTCHA_CLIENT_KEY=your_client_key
```

## Check the balance

Free; confirms the client key is correct:

get_balance.py

```python
"""Check the balance. getBalance is free and is a quick way to verify your clientKey."""
import os

import requests

CLIENT_KEY = os.environ["YESCAPTCHA_CLIENT_KEY"]

r = requests.post(
    "https://api.yescaptcha.com/getBalance",
    json={"clientKey": CLIENT_KEY},
    timeout=30,
)
data = r.json()
if data.get("errorId"):
    # e.g. ERROR_KEY_DOES_NOT_EXIST when the clientKey is wrong
    print("error:", data.get("errorCode"), data.get("errorDescription"))
else:
    print("balance (points):", data["balance"])
```

## Read an image captcha

The synchronous type `ImageToTextTaskOcrBase` returns the result in one request. Sample image: [captcha.jpg](https://docs.yescaptcha.com/samples/captcha.jpg).

quickstart.py

```python
"""First request: read an alphanumeric image captcha with ImageToTextTaskOcrBase
(synchronous, 2 points per image).

Usage:
    export YESCAPTCHA_CLIENT_KEY=your_client_key
    python quickstart.py captcha.png
"""
import base64
import os
import sys

import requests

CLIENT_KEY = os.environ["YESCAPTCHA_CLIENT_KEY"]
image_path = sys.argv[1] if len(sys.argv) > 1 else "captcha.png"

with open(image_path, "rb") as f:
    body = base64.b64encode(f.read()).decode()  # no "data:" prefix

r = requests.post(
    "https://api.yescaptcha.com/createTask",
    json={
        "clientKey": CLIENT_KEY,
        "task": {"type": "ImageToTextTaskOcrBase", "body": body},
    },
    timeout=30,
)
data = r.json()
if data.get("errorId"):
    raise SystemExit(f"{data.get('errorCode')}: {data.get('errorDescription')}")
print("text:", data["solution"]["text"])
```

## A reusable client

Copy this file into your project; it handles both synchronous and asynchronous types:

yescaptcha.py

```python
"""A small client you can copy into your project: create tasks, poll results, check the balance.

    from yescaptcha import YesCaptcha
    client = YesCaptcha(os.environ["YESCAPTCHA_CLIENT_KEY"])
    solution = client.solve({
        "type": "NoCaptchaTaskProxyless",
        "websiteURL": "https://www.google.com/recaptcha/api2/demo",
        "websiteKey": "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
    })
    print(solution["gRecaptchaResponse"])
"""
import os
import time

import requests

class YesCaptchaError(Exception):
    def __init__(self, code, description):
        super().__init__(f"{code}: {description}")
        self.code = code
        self.description = description

class YesCaptcha:
    def __init__(self, client_key, endpoint="https://api.yescaptcha.com", poll_interval=5, timeout=180):
        self.client_key = client_key
        self.endpoint = endpoint.rstrip("/")
        self.poll_interval = poll_interval  # 5 s: polling the same taskId more often only returns "processing"
        self.timeout = timeout
        self.session = requests.Session()

    def call(self, method, payload=None):
        body = {"clientKey": self.client_key, **(payload or {})}
        data = self.session.post(f"{self.endpoint}/{method}", json=body, timeout=30).json()
        if data.get("errorId"):
            raise YesCaptchaError(data.get("errorCode"), data.get("errorDescription"))
        return data

    def balance(self):
        return self.call("getBalance")["balance"]

    def solve(self, task):
        created = self.call("createTask", {"task": task})
        if created.get("status") == "ready":  # synchronous types return the solution directly
            return created["solution"]
        task_id = created["taskId"]
        deadline = time.time() + self.timeout
        while time.time() < deadline:
            time.sleep(self.poll_interval)
            result = self.call("getTaskResult", {"taskId": task_id})
            if result.get("status") == "ready":
                return result["solution"]
        raise TimeoutError(f"task {task_id} not ready after {self.timeout}s")

if __name__ == "__main__":
    client = YesCaptcha(os.environ["YESCAPTCHA_CLIENT_KEY"])
    print("balance:", client.balance())
```

```python
from yescaptcha import YesCaptcha

client = YesCaptcha(os.environ["YESCAPTCHA_CLIENT_KEY"])
solution = client.solve({
    "type": "HCaptchaTaskProxyless",
    "websiteURL": "https://accounts.hcaptcha.com/demo",
    "websiteKey": "a5f74b19-9e45-40e0-b45d-47ff91b7a6c2",
})
print(solution["gRecaptchaResponse"])
```

Errors raise `YesCaptchaError`; `e.code` is the error code (see [Error codes](https://docs.yescaptcha.com/en/api/errors/)).

## Concurrency control

When sending batches, limit how many requests are in progress at once and back off on `ERROR_THREADS_MAXIMUM` (needs `pip install aiohttp`). See [Rate limits](https://docs.yescaptcha.com/en/api/rate-limits/):

concurrency.py

```python
"""Limit concurrency: keep the number of requests waiting for a result below your limit, and back off
on ERROR_THREADS_MAXIMUM (rejected requests are not charged). Requires: pip install aiohttp"""
import asyncio
import os

import aiohttp

CLIENT_KEY = os.environ["YESCAPTCHA_CLIENT_KEY"]
LIMIT = 15  # below your concurrency limit
sem = asyncio.Semaphore(LIMIT)

async def solve(session, task):
    async with sem:  # at most LIMIT requests in progress at a time
        result = {}
        for attempt in range(5):
            async with session.post(
                "https://api.yescaptcha.com/createTask",
                json={"clientKey": CLIENT_KEY, "task": task},
            ) as resp:
                result = await resp.json()
            if result.get("errorCode") != "ERROR_THREADS_MAXIMUM":
                return result
            await asyncio.sleep(1 + attempt)  # over the limit: wait and retry
        return result

async def main(tasks):
    async with aiohttp.ClientSession() as session:
        return await asyncio.gather(*(solve(session, t) for t in tasks))

if __name__ == "__main__":
    print(asyncio.run(main([])))
```

## Submitting reCAPTCHA tokens

-   Submit with requests: [Submit a reCAPTCHA token](https://docs.yescaptcha.com/en/guides/submit-recaptcha-token/#method-1-send-the-request-yourself-recommended)
-   Fill it into the page with Selenium: [Submit a reCAPTCHA token](https://docs.yescaptcha.com/en/guides/submit-recaptcha-token/#method-2-fill-it-into-the-page-selenium-and-similar)
-   Click image challenges with Selenium: [reCAPTCHA image clicking](https://docs.yescaptcha.com/en/guides/recaptcha-click-selenium/), [hCaptcha image clicking](https://docs.yescaptcha.com/en/guides/hcaptcha-click-selenium/)
