Skip to main content

Invisible bot protection for FastAPI applications using Roundtable.ai Proof-of-Human detection

Project description

fastapi-roundtable

Invisible bot protection for FastAPI applications using Roundtable.ai Proof-of-Human detection.

PyPI version Python 3.9+ License: MIT

Overview

fastapi-roundtable is a FastAPI integration for Roundtable.ai Proof-of-Human bot detection. It provides backend session validation for protecting your FastAPI endpoints from automated bots and suspicious activity.

Roundtable uses behavioral biometrics and device analysis to detect bots with best-in-class accuracy (87% vs Google's 69% and Cloudflare's 33%). Unlike CAPTCHAs, it works invisibly in the background, providing a frictionless experience for legitimate users while blocking automated threats.

This library handles the backend validation piece, making it easy to verify session IDs generated by Roundtable's frontend JavaScript tracker.

How It Works

┌─────────────┐
│   Browser   │  1. Roundtable JS tracker monitors user behavior
│  (Frontend) │  2. Generates session ID based on behavioral analysis
└──────┬──────┘
       │ Session ID (form field, header, or body)
       ▼
┌─────────────┐
│   FastAPI   │  3. fastapi-roundtable validates session ID
│  (Backend)  │  4. Queries Roundtable API for risk score
└──────┬──────┘  5. Rejects high-risk sessions automatically
       │
       ▼
┌─────────────┐
│ Roundtable  │  Returns risk score (0-100)
│     API     │  Higher score = more suspicious
└─────────────┘

The result: Bots and automated attacks are blocked before they can exploit your system, while legitimate users never see a CAPTCHA or challenge.

Features

  • Async session validation using aiohttp for high performance
  • Multiple session ID sources - extract from form fields, HTTP headers, or request body
  • Action-based validation - wait for specific user actions to ensure data freshness
  • Configurable risk thresholds - set your own acceptable risk level
  • Type-safe - full type hints throughout
  • Environment variable support - secure API key management
  • FastAPI-native - uses dependency injection for clean integration

Prerequisites

Before using this library, you need:

  1. A Roundtable.ai account with Site Key and Secret Key
  2. Roundtable's JavaScript tracker installed on your frontend pages
  3. A FastAPI application

Installation

pip install fastapi-roundtable

Quick Start

1. Add Roundtable tracker to your frontend

<!DOCTYPE html>
<html>
<head>
    <title>Contact Form</title>
</head>
<body>
    <form method="POST" action="/contact">
        <!-- Hidden field for Roundtable session ID -->
        <input type="hidden" name="rt_session_id" id="rt_session_id" value="">

        <input type="text" name="name" placeholder="Your name" required>
        <input type="email" name="email" placeholder="Your email" required>
        <textarea name="message" placeholder="Your message" required></textarea>
        <button type="submit">Send</button>
    </form>

    <!-- Load Roundtable tracker -->
    <script src="https://cdn.roundtable.ai/v1/rt.js"
            data-site-key="YOUR_SITE_KEY"></script>

    <!-- Populate hidden field with session ID from sessionStorage -->
    <script>
        const interval = setInterval(() => {
            if (sessionStorage.getItem('rtSessionId')) {
                clearInterval(interval);
                document.getElementById("rt_session_id").value = sessionStorage.getItem('rtSessionId');
            }
        }, 500);
    </script>
</body>
</html>

The Roundtable tracker stores the session ID in sessionStorage.rtSessionId. The JavaScript code polls for this value and populates the hidden form field when available.

2. Protect your FastAPI endpoint

from fastapi import Depends, FastAPI
from fastapi_roundtable import Roundtable

app = FastAPI()

# Initialize Roundtable validator (do this once, reuse across endpoints)
roundtable = Roundtable(
    api_key="your_secret_key",  # or set ROUNDTABLE_API_KEY env var
    status_code=404,            # HTTP status to return on validation failure
    max_risk_score=50           # Maximum acceptable risk score (0-100)
)

@app.post("/contact", dependencies=[Depends(roundtable(form_field="rt_session_id"))])
async def contact_form(name: str, email: str, message: str):
    # This code only runs if session validation passes
    # Bots and high-risk sessions are automatically rejected
    return {"message": "Thank you for your message!"}

That's it! Your endpoint is now protected from bots.

Detailed Usage Examples

Form-Based Validation

Perfect for contact forms, signup pages, and user submissions:

from fastapi import Depends, FastAPI, Form
from fastapi_roundtable import Roundtable

app = FastAPI()
roundtable = Roundtable()

@app.post("/signup", dependencies=[Depends(roundtable(form_field="rt_session_id"))])
async def signup(
    username: str = Form(...),
    email: str = Form(...),
    password: str = Form(...)
):
    # Only legitimate users reach this code
    return {"status": "success"}

Header-Based Validation

Ideal for API endpoints and mobile applications:

@app.post("/api/submit", dependencies=[Depends(roundtable(http_header="X-Session-ID"))])
async def api_submit(data: dict):
    # Validate session from custom header
    return {"received": data}

Client sends request with header:

POST /api/submit
X-Session-ID: <roundtable_session_id>

Body-Based Validation

For JSON APIs where session ID is in the request payload:

@app.post("/api/create", dependencies=[Depends(roundtable(body_field="session_id"))])
async def create_resource(data: dict):
    return {"created": True}

Client sends JSON:

{
  "session_id": "<roundtable_session_id>",
  "name": "Resource name",
  "data": {...}
}

Custom Error Handling

Customize the response when validation fails:

from fastapi import HTTPException, Request
from fastapi.responses import HTMLResponse

@app.exception_handler(HTTPException)
async def custom_http_exception_handler(request: Request, exc: HTTPException):
    if exc.status_code == 404:
        return HTMLResponse(
            content="<h1>Access Denied</h1><p>Suspicious activity detected.</p>",
            status_code=404
        )
    return JSONResponse(
        status_code=exc.status_code,
        content={"detail": exc.detail}
    )

Reusing Across Multiple Endpoints

Initialize once, use everywhere:

app = FastAPI()
roundtable = Roundtable(max_risk_score=60)

@app.post("/contact", dependencies=[Depends(roundtable(form_field="rt_session_id"))])
async def contact(): ...

@app.post("/signup", dependencies=[Depends(roundtable(form_field="rt_session_id"))])
async def signup(): ...

@app.post("/api/data", dependencies=[Depends(roundtable(http_header="X-Session-ID"))])
async def api_data(): ...

Ensuring Data Freshness with Validation Timeout

Frontend behavioral data and backend API requests are processed through independent channels. When a user submits a form, the frontend tracker logs the action while the form data is sent to your backend simultaneously. To ensure the risk assessment includes the most recent user activity, this library waits a configurable amount of time before validating the session.

What session_validation_timeout does:

  • Without require_action: Waits for the specified timeout duration to give Roundtable time to process recent frontend events, then validates using the risk score. This is the most common usage pattern.

  • With require_action: Waits for a specific action to appear in the session logs. If the action appears within the timeout period, validates using the risk score. If the timeout is reached without finding the action, the request is blocked (rejected with the configured status code).

Basic Example (waiting for data to settle):

@app.post(
    "/contact",
    dependencies=[
        Depends(
            roundtable(
                form_field="rt_session_id",
                session_validation_timeout=5  # Wait 5 seconds for recent events to process
            )
        )
    ]
)
async def contact_form():
    return {"message": "Thank you!"}

Advanced Example (requiring specific action):

@app.post(
    "/contact",
    dependencies=[
        Depends(
            roundtable(
                form_field="rt_session_id",
                require_action="User submitted contactForm",  # Must see this action
                session_validation_timeout=30  # Wait up to 30 seconds for it
            )
        )
    ]
)
async def contact_form():
    return {"message": "Thank you!"}

Parameters:

  • session_validation_timeout (default: 30 seconds): Time to wait before validating the session. Always applied.
  • require_action (optional): Specific action name that must appear in session logs. If specified and not found within timeout, the request is blocked.

When to use:

  • Without require_action: Most common usage - allows time for recent events to be processed
  • With require_action: When you want to validate that a specific user action occurred (e.g., form submission, button click)
  • Shorter timeouts (5-10s): For better user experience when you expect fast event processing
  • Longer timeouts (30-60s): When you need to ensure even slower events are captured

Configuration

Environment Variables

Store your API key securely:

export ROUNDTABLE_API_KEY="your_secret_key"

Then initialize without explicit key:

roundtable = Roundtable()  # Reads from environment

Parameters

Parameter Type Default Description
api_key str | None None Roundtable API secret key. If not provided, reads from ROUNDTABLE_API_KEY environment variable.
status_code int 404 HTTP status code to raise when validation fails. Using 404 makes bot detection less obvious.
max_risk_score int 50 Maximum acceptable risk score (0-100). Sessions with higher scores are rejected.

Risk Score Guidelines

Risk scores range from 0 (definitely human) to 100 (definitely bot):

  • 0-30: Very likely human - safe to allow
  • 30-50: Probably human - acceptable for most use cases
  • 50-70: Uncertain - higher scrutiny recommended
  • 70-100: Very likely bot - should be blocked

Recommended thresholds:

  • Lenient (max_risk_score=70): Allow most traffic, block obvious bots
  • Balanced (max_risk_score=50): Default setting, good for most applications
  • Strict (max_risk_score=30): High security, may have false positives

Adjust based on your needs. Start with 50 and tune based on your traffic patterns.

Use Cases

Contact Form Spam Prevention

@app.post("/contact", dependencies=[Depends(roundtable(form_field="rt_session_id"))])

Block automated spam submissions while keeping forms simple for real users.

Account Signup Protection

@app.post("/signup", dependencies=[Depends(roundtable(form_field="rt_session_id"))])

Prevent bot-driven account creation and fake registrations.

API Rate Limit Bypass Prevention

@app.post("/api/resource", dependencies=[Depends(roundtable(http_header="X-Session-ID"))])

Stop bots from bypassing rate limits with multiple IPs or identities.

Comment/Review Spam Blocking

@app.post("/reviews", dependencies=[Depends(roundtable(form_field="rt_session_id"))])

Keep your user-generated content authentic and spam-free.

Credential Stuffing Prevention

@app.post("/login", dependencies=[Depends(roundtable(form_field="rt_session_id"))])

Protect login endpoints from automated credential testing attacks.

API Reference

Roundtable

A reusable session validator for FastAPI applications.

class Roundtable:
    def __init__(
        self,
        *,
        api_key: str | None = None,
        status_code: int = 404,
        max_risk_score: int = 50
    ) -> None:
        """Initialize a session validator with Roundtable.ai API credentials."""

__call__(*, form_field: str | None = None, http_header: str | None = None, body_field: str | None = None, require_action: str | None = None, session_validation_timeout: float = 30)

Creates a FastAPI dependency for session validation. Exactly one of form_field, http_header, or body_field must be provided.

Parameters:

  • form_field: Name of the form field containing the session ID
  • http_header: Name of the HTTP header containing the session ID
  • body_field: Name of the request body field containing the session ID
  • session_validation_timeout (default: 30): Seconds to wait before validating the session. Always applied.
  • require_action (optional): Action name that must appear in session logs. If not found within timeout, request is blocked.

Returns: An async dependency function compatible with Depends()

Example:

# Basic validation from form field
Depends(roundtable(form_field="rt_session_id"))

# Wait for specific action before validating
Depends(roundtable(
    form_field="rt_session_id",
    require_action="User submitted contactForm",
    session_validation_timeout=30
))

# Validate from HTTP header
Depends(roundtable(http_header="X-Session-ID"))

# Validate from request body field
Depends(roundtable(body_field="sessionId"))

async validate_session(session_id: str, require_action: str | None = None, session_validation_timeout: float = 60) -> None

Validates a session ID against the Roundtable.ai API. Raises HTTPException if validation fails or timeout is reached without finding the required action.

For advanced usage and full API documentation, see the source code.

Requirements

  • Python 3.9 or higher
  • fastapi
  • aiohttp
  • pydantic

Links

Disclaimer

This library is maintained independently and is not an official Roundtable.ai product. While the author is employed by Roundtable.ai, this project is a community contribution and not affiliated with or endorsed by the company.

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request or open an Issue on GitHub.

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

fastapi_roundtable-0.2.0.tar.gz (9.1 kB view details)

Uploaded Source

Built Distribution

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

fastapi_roundtable-0.2.0-py3-none-any.whl (10.2 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_roundtable-0.2.0.tar.gz.

File metadata

  • Download URL: fastapi_roundtable-0.2.0.tar.gz
  • Upload date:
  • Size: 9.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.2 CPython/3.13.2 Darwin/24.6.0

File hashes

Hashes for fastapi_roundtable-0.2.0.tar.gz
Algorithm Hash digest
SHA256 0ed55f64475353166b074b3a8ce1b3a1e169e7324b705f54926fcaec5c5ea517
MD5 7f696e26fbe9b7f0652dcb8935a74b70
BLAKE2b-256 d6091ca18568e691da3e5a7e69d25e6ffc6f4d5eed14aa4fafb5b1692b682591

See more details on using hashes here.

File details

Details for the file fastapi_roundtable-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: fastapi_roundtable-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 10.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.2 CPython/3.13.2 Darwin/24.6.0

File hashes

Hashes for fastapi_roundtable-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f6e1c45beb60665bb471ae64d4cf22f2eddcb273bcb04530f0f5818318f44a52
MD5 f065168bdef00d62f93e35bd80b32ab5
BLAKE2b-256 34eb4c090e3caf0d574e9bdedc3a9c72a55bd57a5c188211df927d23240caff3

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