Skip to main content

Official Crovly Python SDK — verify captcha tokens

Project description

Crovly Python SDK

Official Python SDK for Crovly — privacy-first, Proof of Work captcha verification.

Installation

pip install crovly

Quick Start

from crovly import Crovly

client = Crovly("crvl_secret_xxx")

# Verify a captcha token from the widget
result = client.verify(token, expected_ip=request_ip)

if result.is_human():
    # Allow the request
    print(f"Human verified (score: {result.score})")
else:
    # Block the request
    print("Verification failed")

Async Usage (FastAPI)

from fastapi import FastAPI, Request, HTTPException
from crovly import AsyncCrovly

app = FastAPI()
crovly = AsyncCrovly("crvl_secret_xxx")

@app.post("/submit")
async def submit_form(request: Request):
    data = await request.json()
    token = data.get("crovly_token")

    if not token:
        raise HTTPException(400, "Missing captcha token")

    result = await crovly.verify(token, expected_ip=request.client.host)

    if not result.is_human():
        raise HTTPException(403, "Captcha verification failed")

    return {"message": "Form submitted successfully"}

@app.on_event("shutdown")
async def shutdown():
    await crovly.close()

Or use the async context manager:

async with AsyncCrovly("crvl_secret_xxx") as client:
    result = await client.verify(token)

Django Middleware

# middleware.py
from crovly import Crovly, CrovlyError

crovly_client = Crovly("crvl_secret_xxx")

PROTECTED_PATHS = ["/contact", "/register", "/login"]

class CrovlyMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if request.method == "POST" and request.path in PROTECTED_PATHS:
            token = request.POST.get("crovly_token")
            if not token:
                from django.http import JsonResponse
                return JsonResponse({"error": "Missing captcha token"}, status=400)

            try:
                result = crovly_client.verify(
                    token,
                    expected_ip=self._get_client_ip(request),
                )
                if not result.is_human():
                    from django.http import JsonResponse
                    return JsonResponse({"error": "Captcha failed"}, status=403)
            except CrovlyError:
                from django.http import JsonResponse
                return JsonResponse({"error": "Verification error"}, status=500)

        return self.get_response(request)

    def _get_client_ip(self, request):
        xff = request.META.get("HTTP_X_FORWARDED_FOR")
        if xff:
            return xff.split(",")[0].strip()
        return request.META.get("REMOTE_ADDR")

Add to settings.py:

MIDDLEWARE = [
    # ...
    "yourapp.middleware.CrovlyMiddleware",
]

Flask

from flask import Flask, request, jsonify
from crovly import Crovly

app = Flask(__name__)
client = Crovly("crvl_secret_xxx")

@app.route("/submit", methods=["POST"])
def submit():
    token = request.form.get("crovly_token")
    if not token:
        return jsonify(error="Missing captcha token"), 400

    result = client.verify(token, expected_ip=request.remote_addr)

    if not result.is_human():
        return jsonify(error="Captcha verification failed"), 403

    return jsonify(message="Success")

Custom Threshold

The default threshold for is_human() is 0.5. You can adjust it:

# Stricter — require higher confidence
if result.is_human(threshold=0.7):
    allow()

# Lenient — allow lower scores
if result.is_human(threshold=0.3):
    allow()

# Manual score check
if result.success and result.score >= 0.8:
    allow()

Configuration

client = Crovly(
    "crvl_secret_xxx",
    api_url="https://api.crovly.com",  # API base URL
    timeout=10.0,                       # Request timeout (seconds)
    max_retries=2,                      # Retries on 5xx/network errors
)

Error Handling

from crovly import (
    Crovly,
    CrovlyError,
    AuthenticationError,
    ValidationError,
    RateLimitError,
    ApiError,
)

client = Crovly("crvl_secret_xxx")

try:
    result = client.verify(token)
except AuthenticationError:
    # Invalid or missing secret key (401)
    print("Check your secret key")
except ValidationError as e:
    # Invalid request parameters (400)
    print(f"Bad request: {e.message}")
except RateLimitError as e:
    # Rate limit exceeded (429)
    print(f"Rate limited, retry after: {e.retry_after}")
except ApiError as e:
    # Server error (5xx)
    print(f"Server error: {e.status_code}")
except CrovlyError as e:
    # Any other API error
    print(f"Error: {e.message} (code: {e.code})")

Error Classes

Class Status When
AuthenticationError 401 Invalid or missing secret key
ValidationError 400 Invalid token or request body
RateLimitError 429 Too many requests
ApiError 5xx Server error (retried automatically)
CrovlyError - Base class for all errors

VerifyResponse

Field Type Description
success bool Whether the token is valid
score float Risk score: 0.0 (bot) to 1.0 (human)
ip str IP address that solved the challenge
solved_at int Unix timestamp in milliseconds

Requirements

  • Python 3.9+
  • httpx

Documentation

Full documentation at docs.crovly.com.

License

MIT

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

crovly-1.0.3.tar.gz (6.4 kB view details)

Uploaded Source

Built Distribution

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

crovly-1.0.3-py3-none-any.whl (8.3 kB view details)

Uploaded Python 3

File details

Details for the file crovly-1.0.3.tar.gz.

File metadata

  • Download URL: crovly-1.0.3.tar.gz
  • Upload date:
  • Size: 6.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for crovly-1.0.3.tar.gz
Algorithm Hash digest
SHA256 44026f50552136074b8c1f6a605ecf8bc84bd765b5e79ab5ac613f299fc3d800
MD5 e50fc986d8249aca3b0af2c36bab0686
BLAKE2b-256 a19fdb6501566edd91fe76a188ffa5c887ba5a0fcca9639e86da60a6a90d5b03

See more details on using hashes here.

File details

Details for the file crovly-1.0.3-py3-none-any.whl.

File metadata

  • Download URL: crovly-1.0.3-py3-none-any.whl
  • Upload date:
  • Size: 8.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for crovly-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ce88e4bda8f5cfa922c4d27b2da79e37ab67c5677a9c48580b51711dcc3882d3
MD5 3919cb14c9b068bcea32358abe2d2c6f
BLAKE2b-256 8e739f16af17663092e740878e82919178e969fe19814742da82041f246c26d3

See more details on using hashes here.

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