Async-first, fully type-hinted, minimal captcha library with adapters for aiogram, FastAPI and Discord.
Project description
captchakit
Async-first, fully type-hinted captcha library for Python 3.10+. Zero runtime deps beyond Pillow. Drop-in adapters for FastAPI, aiogram and discord.py.
Why another one?
| Feature | lepture/captcha |
claptcha |
multicolorcaptcha |
captchakit |
|---|---|---|---|---|
| Async API | ❌ | ❌ | ❌ | ✅ |
py.typed + mypy strict |
⚠️ | ❌ | ❌ | ✅ |
| TTL & attempt tracking | ❌ | ❌ | ❌ | ✅ built-in |
| Pluggable storage | ❌ | ❌ | ❌ | ✅ Protocol |
| Framework adapters | ❌ | ❌ | ❌ | ✅ FastAPI · aiogram · discord.py |
| Runtime deps (core) | +Pillow | +Pillow | +Pillow | +Pillow |
Install
pip install captchakit # core
pip install "captchakit[fastapi]" # + FastAPI adapter
pip install "captchakit[aiogram]" # + aiogram adapter
pip install "captchakit[discord]" # + discord.py adapter (coming in 0.3)
pip install "captchakit[redis]" # + Redis storage backend
discord.pyadapter lands in 0.3 — its extra installs today but the adapter module itself is not yet shipped. See ROADMAP.md.
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
Design
┌─────────────────────────────────────────────────┐
│ Adapters (fastapi / aiogram / discord) │
├─────────────────────────────────────────────────┤
│ CaptchaManager (public facade) │
├──────────────┬──────────────┬───────────────────┤
│ Challenge │ Renderer │ Storage │
│ Text / Math │ Image (PIL) │ Memory / Redis │
└──────────────┴──────────────┴───────────────────┘
Protocol-based, so you can drop in your own storage (Redis, Postgres, Memcached), your own renderer (SVG, WebP, audio), or your own challenge factory (wordlists, emoji grids, …).- Constant-time comparison via
hmac.compare_digest. - Crypto-safe randomness via
secretsfor solution generation. - CPU-bound Pillow drawing offloaded to a worker thread via
asyncio.to_thread, so the event loop never blocks.
Security scope
captchakit is a lightweight human-check, not a bot-farm-grade security layer. It is aimed at Telegram/Discord verification, simple FastAPI registration flows and similar use cases where you just want to raise the cost for casual spam.
For high-value forms (login, payment) use hCaptcha, Cloudflare Turnstile or reCAPTCHA Enterprise in addition. Combine captchakit with proper rate limiting at the edge of your application.
Roadmap
See ROADMAP.md for the full development plan.
| Version | Highlights |
|---|---|
| 0.1 (MVP) | Core + Text/Math challenges + Image renderer + Memory storage + FastAPI |
| 0.2 ✅ | aiogram adapter + RedisStorage + EmojiGridChallenge |
| 0.3 | discord.py adapter + AudioChallenge (optional) + WordChallenge + docs site |
| 0.4 | i18n + theming + metrics hooks |
| 1.0 | Stable API, semver commitment |
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
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file captchakit-0.2.0.tar.gz.
File metadata
- Download URL: captchakit-0.2.0.tar.gz
- Upload date:
- Size: 22.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f344859c14352b3e4f095950d0421b1576c39d53c6a66c93786b7da54763918c
|
|
| MD5 |
ffffaecde9907ebd0daf6de783d99fb5
|
|
| BLAKE2b-256 |
bc436a36ff77e82651b86f54a7f34bfafa4c301cfd2c5a10723b67b9f64c54ea
|
Provenance
The following attestation bundles were made for captchakit-0.2.0.tar.gz:
Publisher:
release.yml on akerem16/captchakit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
captchakit-0.2.0.tar.gz -
Subject digest:
f344859c14352b3e4f095950d0421b1576c39d53c6a66c93786b7da54763918c - Sigstore transparency entry: 1342130717
- Sigstore integration time:
-
Permalink:
akerem16/captchakit@621c91d2fa97a72021f9f6cedd21105c9c5907cf -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/akerem16
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@621c91d2fa97a72021f9f6cedd21105c9c5907cf -
Trigger Event:
push
-
Statement type:
File details
Details for the file captchakit-0.2.0-py3-none-any.whl.
File metadata
- Download URL: captchakit-0.2.0-py3-none-any.whl
- Upload date:
- Size: 20.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd1fa8cfb727ea8654834eced363c962061b8df6c97c3894deb0be248bcbf91e
|
|
| MD5 |
403c0efcf09461eaab17d8b021c13e6c
|
|
| BLAKE2b-256 |
a164c1b68027acbb58a5c309bc59bfc087cd064a9196e58ce1f1843f360134c0
|
Provenance
The following attestation bundles were made for captchakit-0.2.0-py3-none-any.whl:
Publisher:
release.yml on akerem16/captchakit
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
captchakit-0.2.0-py3-none-any.whl -
Subject digest:
cd1fa8cfb727ea8654834eced363c962061b8df6c97c3894deb0be248bcbf91e - Sigstore transparency entry: 1342130914
- Sigstore integration time:
-
Permalink:
akerem16/captchakit@621c91d2fa97a72021f9f6cedd21105c9c5907cf -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/akerem16
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@621c91d2fa97a72021f9f6cedd21105c9c5907cf -
Trigger Event:
push
-
Statement type: