Metadata-Version: 2.4
Name: captchasonic
Version: 1.1.0
Summary: Official CaptchaSonic Python SDK — modern, async-ready, gRPC + HTTP
Project-URL: Homepage, https://captchasonic.com
Project-URL: Documentation, https://docs.captchasonic.com
Project-URL: Repository, https://github.com/captchasonic/sonic-sdk
Project-URL: Bug Tracker, https://github.com/captchasonic/sonic-sdk/issues
Project-URL: Changelog, https://github.com/captchasonic/sonic-sdk/releases
Author-email: CaptchaSonic <support@captchasonic.com>
Maintainer-email: CaptchaSonic <support@captchasonic.com>
License-Expression: MIT
License-File: LICENSE
Keywords: antibot,automation,aws-waf,captcha,captcha-solver,captchasonic,cloudflare,geetest,grpc,popularcaptcha,recaptcha,tiktok,turnstile
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: grpcio>=1.60.0
Requires-Dist: protobuf>=7.0.0
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: grpcio-tools>=1.60.0; extra == 'dev'
Requires-Dist: httpx>=0.27.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Provides-Extra: http
Requires-Dist: httpx>=0.27.0; extra == 'http'
Description-Content-Type: text/markdown

# captchasonic

Official CaptchaSonic library for Python.

The CaptchaSonic Python SDK is a modern, async-ready library designed for high-throughput automation. It features first-class type hints, dual-transport flexibility (gRPC + HTTP), and automatic retry logic.

Works in **Python 3.10+** — scripts, notebooks, Django, FastAPI, Flask, and CLI tools.

---

## Installation

```bash
pip install captchasonic
```

For HTTP transport (optional):

```bash
pip install "captchasonic[http]"
```

---

## Quick Start

```python
from captchasonic import CaptchaSonic

solver = CaptchaSonic("YOUR_API_KEY")

result = solver.solve_recaptcha_v2(
    images=[tile1, tile2, tile3],
    question="traffic lights",
)

print(result["typed_solution"]["grid"]["objects"])  # [2, 4, 7]
```

---

## Usage — Geetest (nine-grid)

```python
from captchasonic import CaptchaSonic

solver = CaptchaSonic("YOUR_API_KEY")

try:
    result = solver.solve_geetest(
        geetest_type="nine",
        question="Select all bicycles",
        images=tiles,            # list[bytes | Path | str]
    )
    print("Solution:", result["typed_solution"])
except Exception as err:
    print("Error:", err)
```

---

## Async Usage

The SDK ships a full async client with identical methods.

```python
import asyncio
from captchasonic import AsyncCaptchaSonic

async def main():
    async with AsyncCaptchaSonic("YOUR_API_KEY") as solver:
        result = await solver.solve_geetest(
            geetest_type="nine",
            question="Select all bicycles",
            images=tiles,
        )
        print("Solution:", result["typed_solution"])

asyncio.run(main())
```

---

## Configuration

```python
solver = CaptchaSonic(
    "YOUR_API_KEY",
    transport="grpc",           # "grpc" (default) or "http"
    url="api.captchasonic.com:443",  # Override the API endpoint. Optional.
    base_url=None,              # Alias for url. Optional.
    timeout=30.0,               # Per-call timeout in seconds. Default: 30.
    polling_interval=2.0,       # Seconds between task polls. Default: 2.
    polling_timeout=120.0,      # Max seconds to wait for async task. Default: 120.
    secure=True,                # Use TLS for gRPC. Default: True.
)
```

**Transport guide:**

| Transport | Environments | Protocol |
|---|---|---|
| `grpc` (default) | Server-side Python | gRPC binary over HTTP/2 — lowest latency |
| `http` | Universal | Plain REST/JSON via `httpx` |

> `grpc` sends images as raw binary — zero base64 overhead.
> `http` auto-encodes images to base64 before sending.

---

## Solve Methods

All methods accept keyword arguments with full type hints and docstrings.

### `solve_popular_captcha()`

```python
solver.solve_popular_captcha(
    images=list[ImageInput],
    question=str,                # e.g. "Select all traffic lights"
    question_type=str,           # "objectClassify" | "objectClick" | "objectDrag" | "grid"
    examples=list[ImageInput],   # optional
    screenshot=bool,             # optional, default False
    website_url=str,             # optional
    website_key=str,             # optional
)
```

### `solve_recaptcha_v2()`

```python
solver.solve_recaptcha_v2(
    images=list[ImageInput],
    question=str,                # "traffic lights" or "/m/015qff"
    question_type=str,           # "split_33" | "33" | "44". Optional.
    website_url=str,             # optional
    website_key=str,             # optional
)
```

### `solve_geetest()`

```python
solver.solve_geetest(
    geetest_type=str,            # "nine" | "click" | "slide" | "match" | "winlinze"
    question=str,                # required for "nine" and "click"
    images=list[ImageInput],     # required for "nine", "click", "slide"
    examples=list[ImageInput],   # optional
    gtv=int,                     # Geetest version (e.g. 3). Optional.
    website_url=str,             # optional
)

# Type aliases also accepted:
# "nine"     → "geetest_nine" | "9"
# "click"    → "geetest_click" | "icon"
# "slide"    → "geetest_slide"
# "match"    → "geetest_match"
# "winlinze" → "geetest_winlinze"
```

### `solve_ocr()`

```python
solver.solve_ocr(
    images=list[ImageInput],
    module=str,                  # "common" | "mtcaptcha" | "bls" | "morocco". Optional.
    numeric=bool,                # Expect only digits. Optional.
    case_sensitive=bool,         # Preserve letter case. Optional.
    min_length=int,              # Minimum text length. Optional.
    max_length=int,              # Maximum text length. Optional.
    website_url=str,             # optional
)
# Returns: result["typed_solution"]["text"]["texts"][0]
```

### `solve_tiktok()`

```python
solver.solve_tiktok(
    type=str,                    # "click" | "whirl" | "slide" (or "tiktok_click" etc.)
    question=str,                # optional
    images=list[ImageInput],
    examples=list[ImageInput],   # required for "whirl" and "slide"
    website_url=str,             # optional
)
```

### `solve_binance()`

```python
solver.solve_binance(
    type=str,                    # "grid" | "slide" (or "binance_grid" etc.)
    question=str,                # required for "grid"
    images=list[ImageInput],
    examples=list[ImageInput],   # optional
    website_url=str,             # optional
)
```

### `solve_aws_waf()`

```python
solver.solve_aws_waf(
    images=list[ImageInput],
    question=str,                # e.g. "grid:vehicles:cars"
    website_url=str,             # optional
    website_key=str,             # optional
)
```

### `solve_slide_image()`

```python
solver.solve_slide_image(images=[background, piece])
# Returns: result["typed_solution"]["slide"]["x"] — pixel offset
```

### Token Automation Methods

These submit a task and poll until a token is returned (up to 120 s).

```python
solver.solve_turnstile(website_url=..., website_key=..., proxy=...)
solver.solve_popular_captcha_token(website_url=..., website_key=..., proxy=..., metadata=...)
solver.solve_recaptcha_v2_token(website_url=..., website_key=..., proxy=...)
solver.solve_recaptcha_v3_token(website_url=..., website_key=..., proxy=...)
solver.solve_cloudflare(website_url=..., website_key=..., proxy=...)  # proxy required
```

---

## Account Methods

```python
balance = solver.get_balance()    # → float (USD)
solver.health_check()             # → HealthCheckResponse
```

---

## Image Input

```python
from pathlib import Path

ImageInput = bytes | str | Path | BinaryIO

# File path
solver.solve_ocr(images=[Path("captcha.png")])
solver.solve_ocr(images=["./captcha.png"])

# Raw bytes
with open("captcha.png", "rb") as f:
    solver.solve_ocr(images=[f.read()])
```

> `grpc` sends images as raw binary — zero base64 overhead.
> `http` auto-encodes images to base64 before sending.

---

## Error Handling

```python
from captchasonic import CaptchaSonic
from captchasonic.exceptions import SonicError

try:
    result = solver.solve_geetest(geetest_type="nine", question="bicycles", images=tiles)
except SonicError as err:
    print(err.error_id)    # 1–6
    print(type(err).__name__)  # "InvalidApiKeyError" | "InsufficientBalanceError" | ...
    print(err)
```

| `error_id` | Exception | Meaning |
|---|---|---|
| 1 | `InvalidApiKeyError` | API key not valid |
| 2 | `InsufficientBalanceError` | Insufficient credits |
| 3 | `DailyLimitExceededError` | Daily quota exceeded |
| 4 | `MinuteLimitExceededError` | Rate limit hit |
| 5 | `QuotaExceededError` | Plan quota exceeded |
| 6 | `PlanExpiredError` | Subscription expired |

Transient gRPC errors are retried automatically with exponential backoff — up to 3 attempts.

---

## Context Manager

```python
# Sync — auto-closes gRPC channel / HTTP session
with CaptchaSonic("YOUR_API_KEY") as solver:
    result = solver.solve_ocr(images=[Path("captcha.png")])
    print(result["typed_solution"]["text"]["texts"][0])

# Async
async with AsyncCaptchaSonic("YOUR_API_KEY") as solver:
    result = await solver.solve_ocr(images=[Path("captcha.png")])
```

---

## Requirements

- **Python** ≥ 3.10
- **gRPC transport** — `grpcio`, `protobuf` (included)
- **HTTP transport** — `httpx` (install with `pip install captchasonic[http]`)

---

## License

MIT — see [LICENSE](./LICENSE)
