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.1.tar.gz (6.3 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.1-py3-none-any.whl (8.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: crovly-1.0.1.tar.gz
  • Upload date:
  • Size: 6.3 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.1.tar.gz
Algorithm Hash digest
SHA256 2c4a67bb675fe34be14709b60ba6f6e87f40bc7234413fd07cf442633816dcb2
MD5 21a0d84deff226159afb7bf0446ecf76
BLAKE2b-256 3fbbf853fb7614311cbb28c43ca60a481c1fcb8ef548f6babeca9c8cae98554e

See more details on using hashes here.

File details

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

File metadata

  • Download URL: crovly-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 8.2 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 45f2cf959ede5676f7b6a1871d1ea360cc9b10e78cb472612d506104ee33268d
MD5 e404d796a164c3f9a6f22c789d4ceeab
BLAKE2b-256 c1779b6a315ffef872d315a9545cfdfcee1d048d019a08749e381929bfe4d972

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