Skip to main content

Async-first, fully type-hinted, minimal captcha library with adapters for aiogram, FastAPI and Discord.

Project description

captchakit

Production-ready, async-first captcha library for Python 3.10+. Zero runtime deps beyond Pillow. Drop-in adapters for FastAPI, aiogram, Discord and Django — plus Redis / Postgres storage, rate limiting and Prometheus metrics.

PyPI Python CI mypy: strict Ruff Stable API License: MIT


Why captchakit

Feature lepture/captcha claptcha multicolorcaptcha captchakit
Async API
py.typed + mypy strict ⚠️
TTL & attempt tracking ✅ built-in
Pluggable storage Protocol — Memory / Redis / Postgres
Pluggable rate limiter Protocol — in-memory & Redis token-bucket
Prometheus metrics ✅ opt-in
Framework adapters ✅ FastAPI · aiogram · Discord · Django
Audio challenge (a11y) AudioRenderer
i18n prompt hooks ✅ en / tr / de / es + custom catalog
Core runtime deps +Pillow +Pillow +Pillow +Pillow

Install

pip install captchakit                  # core

# adapters
pip install "captchakit[fastapi]"
pip install "captchakit[aiogram]"
pip install "captchakit[discord]"
pip install "captchakit[django]"

# storage
pip install "captchakit[redis]"         # + rate-limit token bucket
pip install "captchakit[postgres]"

# observability
pip install "captchakit[metrics]"       # Prometheus adapter

30-second example

import asyncio
from captchakit import (
    CaptchaManager, ImageRenderer, MemoryStorage, TextChallengeFactory,
)

async def main() -> None:
    manager = CaptchaManager(
        factory=TextChallengeFactory(length=5),
        renderer=ImageRenderer(),
        storage=MemoryStorage(),
        ttl=120.0,
        max_attempts=3,
    )
    challenge_id, png_bytes = await manager.issue()
    # ... show png_bytes to the user, receive their answer ...
    ok = await manager.verify(challenge_id, user_input="ABCDE")
    print("verified" if ok else "wrong answer, more attempts remain")

asyncio.run(main())

FastAPI in 10 lines

from fastapi import Depends, FastAPI
from captchakit import CaptchaManager, ImageRenderer, MathChallengeFactory, MemoryStorage
from captchakit.adapters.fastapi import captcha_router, verify_captcha

manager = CaptchaManager(MathChallengeFactory(), ImageRenderer(), MemoryStorage())
app = FastAPI()
app.include_router(captcha_router(manager, prefix="/captcha"))

@app.post("/register")
async def register(_: None = Depends(verify_captcha(manager))) -> dict[str, bool]:
    return {"ok": True}

Run the bundled demo:

uv run python -m uvicorn examples.fastapi_login:app --reload
# open http://127.0.0.1:8000

Architecture

┌──────────────────────────────────────────────────────────────┐
│  Adapters (FastAPI / aiogram / Discord / Django)             │
├──────────────────────────────────────────────────────────────┤
│  CaptchaManager  (issue → render → persist · verify · TTL)   │
├──────────────┬──────────────┬───────────────┬────────────────┤
│  Challenge   │  Renderer    │  Storage      │  Rate limiter  │
│  Text · Math │  Image · SVG │  Memory       │  NoOp          │
│  Grid · Word │  Audio       │  Redis · PG   │  Token bucket  │
└──────────────┴──────────────┴───────────────┴────────────────┘
             ↓ i18n translator · metrics sink · clock

Everything coloured here is a Protocol — drop in your own implementation without subclassing.

  • Constant-time comparison via hmac.compare_digest.
  • Crypto-safe randomness via secrets for solution generation.
  • CPU-bound Pillow drawing offloaded to a worker thread with asyncio.to_thread.
  • Multi-process safe when paired with RedisStorage or PostgresStorage.

Performance

Operation Mean (ms) Median (ms) p99 (ms)
ImageRenderer.render (PNG) 3.64 3.54 4.84
SVGRenderer.render (SVG) 0.03 0.03 0.05
AudioRenderer.render (WAV) 2.69 2.60 3.72
issue + verify round-trip 2.73 2.57 5.29

Measured on a single CPython 3.13 thread, Windows 10, 500 iterations after 20 warmups. Reproduce with uv run python benchmarks/bench.py.

Production deployment

  • Run behind a proper reverse proxy with TLS and WAF rules.
  • Use RedisStorage or PostgresStorage if you scale beyond a single worker — MemoryStorage is per-process.
  • Wire RateLimiter to RedisTokenBucket (or your edge WAF) when exposing the issue endpoint publicly.
  • Expose PrometheusMetrics on :9090/metrics and alert on captchakit_too_many_attempts_total spikes.
  • Pair at least one visual renderer with AudioRenderer for accessibility.
  • Set ttl short (60–180 s) and max_attempts low (2–3) — captchakit is a raise-the-cost layer, not a fortress.

Security scope

captchakit is a lightweight human-check — it raises the cost for casual spam and scripted abuse. It is not a bot-farm-grade defence.

For high-value forms (payment, password reset, account takeover) use hCaptcha, Cloudflare Turnstile or reCAPTCHA Enterprise in addition to captchakit, and enforce rate limiting at the edge of your application.

Vulnerability reports: see SECURITY.md.

Stability & compatibility

  • 1.x is API-stable — public symbols follow semver. See docs/stability.md for the policy.
  • Supported Python versions: 3.10 → 3.13. New 3.x is added within one MINOR of upstream release.
  • Every MINOR gets security patches throughout the life of the 1.x line.

Documentation

Full docs at https://akerem16.github.io/captchakit/ — quickstart, adapter guides, storage & rate-limit recipes, i18n, metrics, accessibility, API reference.

Contributing

Contributions welcome. Local development:

git clone https://github.com/akerem16/captchakit
cd captchakit
uv sync --all-extras
uv run ruff check .
uv run mypy
uv run pytest
uv run bandit -c pyproject.toml -r src
uv run pip-audit

See CONTRIBUTING.md.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

captchakit-1.0.0.tar.gz (31.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

captchakit-1.0.0-py3-none-any.whl (39.8 kB view details)

Uploaded Python 3

File details

Details for the file captchakit-1.0.0.tar.gz.

File metadata

  • Download URL: captchakit-1.0.0.tar.gz
  • Upload date:
  • Size: 31.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for captchakit-1.0.0.tar.gz
Algorithm Hash digest
SHA256 7d782f2133935e038eb3b8dec9f7bbd1f7641463b2b59b7cc864e54addecd6de
MD5 a7f0ea3e28c8a8e26acfe8bf008bc4fd
BLAKE2b-256 507d883a411101e19f50218632bdf004b5488afe4c3ad21b796f50cdc0c70478

See more details on using hashes here.

Provenance

The following attestation bundles were made for captchakit-1.0.0.tar.gz:

Publisher: release.yml on akerem16/captchakit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file captchakit-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: captchakit-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 39.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for captchakit-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 78c271363c2807bde70d8fa2857b448201561dc388927ce9190550e711aaa885
MD5 128dc5ccd43a892b10d3b1160e52d467
BLAKE2b-256 7f9cee993aaeb3b1df774d21755c2c10eed2317b67c51df0ebc0c44b1b3e07b8

See more details on using hashes here.

Provenance

The following attestation bundles were made for captchakit-1.0.0-py3-none-any.whl:

Publisher: release.yml on akerem16/captchakit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page