Metadata-Version: 2.4
Name: ghostcaptcha-client
Version: 1.4.1
Summary: Official Python client for GhostCaptcha API — sync/async captcha solving
Home-page: https://github.com/ghostcaptchareal/ghostcaptcha-client-python
Author: GhostCaptcha
Author-email: GhostCaptcha <support@ghostcaptcha.xyz>
License: MIT
Project-URL: Homepage, https://ghostcaptcha.xyz
Project-URL: Documentation, https://ghostcaptcha.xyz/docs
Project-URL: Repository, https://github.com/ghostcaptchareal/ghostcaptcha-client-python
Project-URL: Bug Tracker, https://github.com/ghostcaptchareal/ghostcaptcha-client-python/issues
Project-URL: Changelog, https://github.com/ghostcaptchareal/ghostcaptcha-client-python/releases
Keywords: hcaptcha,captcha,solver,api,ghostcaptcha,async
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.27.0
Provides-Extra: curl
Requires-Dist: pycurl>=7.45; extra == "curl"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: httpx>=0.27.0; extra == "dev"
Provides-Extra: all
Requires-Dist: ghostcaptcha-client[curl,dev]; extra == "all"
Dynamic: author
Dynamic: home-page
Dynamic: requires-python

<p align="center">
  <img src="https://ghostcaptcha.xyz/logo.png" alt="GhostCaptcha" width="100"/>
</p>

<h1 align="center">GhostCaptcha Client</h1>

<p align="center">
  <strong>Production-grade Python client for hCaptcha solving via GhostCaptcha API</strong>
  <br>
  Sync · Async · Browser Solver · Batch Processing · Movement Simulation
</p>

<p align="center">
  <a href="https://pypi.org/project/ghostcaptcha-client/"><img src="https://img.shields.io/pypi/v/ghostcaptcha-client?color=8B5CF6&logo=pypi&logoColor=white" alt="PyPI"></a>
  <a href="https://pypi.org/project/ghostcaptcha-client/"><img src="https://img.shields.io/pypi/pyversions/ghostcaptcha-client?color=8B5CF6" alt="Python"></a>
  <a href="https://github.com/ghostcaptchareal/ghostcaptcha-client-python"><img src="https://img.shields.io/github/stars/ghostcaptchareal/ghostcaptcha-client-python?color=8B5CF6&logo=github" alt="Stars"></a>
  <a href="https://github.com/ghostcaptchareal/ghostcaptcha-client-python/actions"><img src="https://img.shields.io/github/actions/workflow/status/ghostcaptchareal/ghostcaptcha-client-python/ci.yml?color=8B5CF6" alt="CI"></a>
  <a href="https://github.com/ghostcaptchareal/ghostcaptcha-client-python/blob/main/LICENSE"><img src="https://img.shields.io/github/license/ghostcaptchareal/ghostcaptcha-client-python?color=8B5CF6" alt="License"></a>
</p>

---

## Overview

GhostCaptcha Client is a **production-grade, security-hardened** Python library for solving hCaptcha challenges through the GhostCaptcha API. It provides both synchronous and asynchronous clients, automatic retry with backoff, circuit breaker pattern, and comprehensive error handling.

**What makes it different?**
- **Dual sync/async** — Same API surface for both paradigms
- **Security-first** — Magic byte validation, path traversal protection, proxy URL sanitization, key masking
- **Circuit breaker** — Automatic failure detection prevents cascade failures
- **Batch processing** — Concurrent solver for high-throughput workloads
- **Browser solver** — Multi-image classification with `objects[]`, `clicks[]`, `box[]` responses
- **Movement Simulator** — Human-like mouse trajectory generation for any application

---

## Quick Start

```bash
pip install ghostcaptcha-client
```

```python
from ghostcaptcha_client import GhostCaptchaClient

client = GhostCaptchaClient("hcap-your-api-key")

with client:
    result = client.solve_file("captcha.png")
    print(result)

    balance = client.get_balance()
    print(f"Balance: {balance.get('balance')}")
```

Async:

```python
import asyncio
from ghostcaptcha_client import AsyncGhostCaptchaClient

async def main():
    async with AsyncGhostCaptchaClient("hcap-your-api-key") as client:
        result = await client.solve_file("captcha.png")
        print(result)

asyncio.run(main())
```

---

## Installation

| Method | Command |
|--------|---------|
| **Stable** | `pip install ghostcaptcha-client` |
| **With curl transport** | `pip install ghostcaptcha-client[curl]` |
| **Development** | `pip install ghostcaptcha-client[dev]` |
| **Latest** | `pip install git+https://github.com/ghostcaptchareal/ghostcaptcha-client-python.git` |

**Requirements:** Python 3.9+, httpx (auto-installed)

---

## Core Features

### Captcha Solving

```python
# From raw bytes
result = client.solve(image_bytes)

# From file
result = client.solve_file("captcha.png")

# From base64
result = client.solve_base64("iVBORw0KGgo...")
```

### GhostCaptcha-Compatible API

```python
# Create a task
task_id = client.create_task({
    "type": "HCaptchaClassification",
    "queries": ["base64_image_data"],
})

# Wait for result (auto-polls)
result = client.wait_for_result(task_id, poll_interval=0.5, timeout=120)

# Check balance
balance = client.get_balance()
```

### Browser Solver (Multi-Image)

```python
from ghostcaptcha_client import BrowserSolver

solver = BrowserSolver("hcap-your-api-key")

# URL-based images (hCaptcha CDN)
result = solver.solve_urls(
    image_urls=["https://imgs3.hcaptcha.com/..."],
    question="click all images containing a bus",
    response_type="objects",  # "objects" | "clicks" | "box"
)
print(result["objects"])      # [True, False, True, ...]
print(result["confidences"])  # [0.95, 0.0, 0.88, ...]
```

### Async Browser Solver

```python
from ghostcaptcha_client import AsyncBrowserSolver

async with AsyncBrowserSolver("hcap-your-api-key") as solver:
    result = await solver.solve_urls(
        image_urls=["https://imgs3.hcaptcha.com/..."],
        question="click all cars",
        response_type="clicks",
    )
```

### Movement Simulator

```python
from ghostcaptcha_client import MovementSimulator

sim = MovementSimulator()

# Single point-to-point movement
result = sim.simulate_movement(
    start={"x": 100, "y": 200},
    end={"x": 400, "y": 350},
    duration_ms=300,
)
for point in result["trajectory"]:
    mouse.move_to(point["x"], point["y"])

# Multi-click sequence
result = sim.simulate_clicks(
    clicks=[{"x": 260, "y": 184}, {"x": 416, "y": 420}],
)
for phase in result["sequence"]:
    if phase["phase"] == "move":
        for pt in phase["trajectory"]:
            mouse.move_to(pt["x"], pt["y"])
    elif phase["phase"] == "click":
        mouse.click_at(phase["x"], phase["y"])
        time.sleep(phase["hold_ms"] / 1000)
```

Generates human-like mouse trajectories with realistic easing and micro-corrections.
API: `POST /v1/simulateMouseMovement/simulate` and `/clicks`.

### Batch Processing

```python
from ghostcaptcha_client import BatchSolver

batch = BatchSolver("hcap-your-api-key", max_workers=10)

# Sync batch
results = batch.solve_files(["img1.png", "img2.png", "img3.png"])

# Async batch
results = await batch.solve_files_async(["img1.png", "img2.png", "img3.png"])

# Each result: {"path": "...", "success": True, "result": {...}}
```

---

## Security Features

| Feature | Description |
|---------|-------------|
| **Magic Byte Validation** | Rejects non-image data (PNG/JPEG/GIF/WebP/BMP only) before upload |
| **Path Traversal Protection** | Resolves and validates file paths safely |
| **Image Size Limit** | 10MB max per upload |
| **Key Length Limit** | 256 character max for API keys |
| **Key Masking** | Keys are masked in logs, `repr()`, and error messages |
| **Proxy URL Validation** | Strict format check prevents SSRF |
| **Circuit Breaker** | 5 consecutive failures → 60s cooloff, auto-resets |
| **Privacy Mode** | Device registration disabled by default |

---

## Error Handling

```python
from ghostcaptcha_client import (
    GhostCaptchaError, AuthError, RateLimitError,
    BalanceError, NetworkError, CircuitBreakerOpenError,
)

try:
    result = client.solve_file("captcha.png")
except AuthError:
    print("Invalid API key")
except RateLimitError as e:
    print(f"Rate limited, retry after {e.retry_after}s")
except BalanceError:
    print("Insufficient balance")
except NetworkError:
    print("Connection error")
except CircuitBreakerOpenError as e:
    print(f"Circuit open, retry after {e.retry_after:.0f}s")
except GhostCaptchaError as e:
    print(f"[{e.code}] {e.description}")
```

| Exception | HTTP | Cause |
|-----------|------|-------|
| `AuthError` | 401 | Invalid/missing API key |
| `RateLimitError` | 429 | Too many requests (auto-retries) |
| `BalanceError` | 402 | Insufficient credits |
| `NetworkError` | — | Connection or timeout |
| `CircuitBreakerOpenError` | 503 | Too many consecutive failures |
| `GhostCaptchaError` | * | Base exception for all API errors |

---

## Configuration

```python
client = GhostCaptchaClient(
    api_key="hcap-xxx...",
    base_url="https://api.ghostcaptcha.xyz/v1",     # or GHOSTCAPTCHA_API_URL env
    verify_ssl=True,
    timeout=60,
    proxy="http://user:pass@host:8080",               # Optional proxy
    transport="httpx",                                # "httpx" or "curl"
    privacy_mode=True,                                # Skip device registration
)

# Runtime configuration
client.enable_debug()
client.set_proxy("http://new-proxy:8080")
```

---

## Context Managers

```python
# Sync — auto-closes connection pool
with GhostCaptchaClient("hcap-xxx...") as client:
    balance = client.get_balance()

# Async — auto-closes connection pool
async with AsyncGhostCaptchaClient("hcap-xxx...") as client:
    balance = await client.get_balance()
```

---

## API Reference

| Method | Returns | Description |
|--------|---------|-------------|
| `solve()` | `dict` | Solve from raw image bytes |
| `solve_file()` | `dict` | Solve from image file path |
| `solve_base64()` | `dict` | Solve from base64-encoded image |
| `create_task()` | `str` | Create solving task (taskId) |
| `get_task_result()` | `dict` | Poll task status |
| `wait_for_result()` | `dict` | Poll until ready or timeout |
| `get_balance()` | `dict` | Check account balance |
| `get_soft_id()` | `str` | Get Soft ID |
| `health()` | `dict` | Check API server health |
| `enable_debug()` | — | Toggle debug logging |
| `set_proxy()` | — | Change proxy at runtime |
| `simulate_movement()` | `dict` | Generate human-like mouse trajectory |
| `simulate_clicks()` | `dict` | Generate click sequence with movement |

---

## Development

```bash
# Clone
git clone https://github.com/ghostcaptchareal/ghostcaptcha-client-python.git
cd ghostcaptcha-client-python

# Install dev deps
pip install -e ".[dev]"

# Run tests
pytest tests/ -v

# Build
pip install build twine
python -m build
```

---

## License

MIT — see [LICENSE](https://github.com/ghostcaptchareal/ghostcaptcha-client-python/blob/main/LICENSE).

---

<p align="center">
  <a href="https://ghostcaptcha.xyz">ghostcaptcha.xyz</a>
  ·
  <a href="https://github.com/ghostcaptchareal/ghostcaptcha-client-python">GitHub</a>
  ·
  <a href="https://pypi.org/project/ghostcaptcha-client/">PyPI</a>
  ·
  <a href="https://ghostcaptcha.xyz/docs">Documentation</a>
</p>
