Skip to main content

Arcjet Python SDK. Bot detection, rate limiting, email validation, attack protection for Python applications.

Project description

Arcjet Logo

Arcjet - Python SDK

PyPI badge

Arcjet helps developers protect their apps in just a few lines of code. Bot detection. Rate limiting. Email validation. Attack protection. A developer-first approach to security.

This is the monorepo containing various Arcjet open source packages for Python.

Features

Arcjet security features for protecting Python apps:

  • 🤖 Bot protection - manage traffic by automated clients and bots.
  • 🛑 Rate limiting - limit the number of requests a client can make.
  • 🛡️ Shield WAF - protect your application against common attacks.
  • 📧 Email validation - prevent users from signing up with fake email addresses.
  • 📝 Signup form protection - combines rate limiting, bot protection, and email validation to protect your signup forms.

[!NOTE] The Arcjet Python SDK currently doesn't support sensitive information detection or request filters. These features are planned for a future release when local analysis will be supported.

Get help

Join our Discord server or reach out for support.

Installation

Install from PyPI with uv:

# With uv
uv install arcjet

Usage

Read the docs at docs.arcjet.com

Quick start example

This example implements Arcjet bot protection, rate limiting, email validation, and Shield WAF in a FastAPI application. Requests from bots not in the allow list will be blocked with a 403 Forbidden response.

The example email is invalid so an error will be returned - change the email to see different results.

FastAPI

An asynchronous example using FastAPI with the Arcjet async client.

# main.py
import os
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

from arcjet import (
    arcjet,
    shield,
    detect_bot,
    token_bucket,
    Mode,
    BotCategory,
)

app = FastAPI()

aj = arcjet(
    key=os.environ["ARCJET_KEY"],  # Get your key from https://app.arcjet.com
    rules=[
        # Shield protects your app from common attacks e.g. SQL injection
        shield(mode=Mode.LIVE),
        # Create a bot detection rule
        detect_bot(
            mode=Mode.LIVE, allow=[
                BotCategory.SEARCH_ENGINE,  # Google, Bing, etc
                # Uncomment to allow these other common bot categories
                # See the full list at https://arcjet.com/bot-list
                # BotCategory.MONITOR", // Uptime monitoring services
                # BotCategory.PREVIEW", // Link previews e.g. Slack, Discord
            ]
        ),
        # Create a token bucket rate limit. Other algorithms are supported
        token_bucket(
            # Tracked by IP address by default, but this can be customized
            # See https://docs.arcjet.com/fingerprints
            # characteristics: ["ip.src"],
            mode=Mode.LIVE,
            refill_rate=5,  # Refill 5 tokens per interval
            interval=10,  # Refill every 10 seconds
            capacity=10  # Bucket capacity of 10 tokens
        ),
    ],
)


@app.get("/")
async def hello(request: Request):
    # Call protect() to evaluate the request against the rules
    decision = await aj.protect(
        request, requested=5  # Deduct 5 tokens from the bucket
    )

    # Handle denied requests
    if decision.is_denied():
        status = 429 if decision.reason.is_rate_limit() else 403
        return JSONResponse(
            {"error": "Denied", "reason": decision.reason.to_dict()},
            status_code=status,
        )

    # Check IP metadata (VPNs, hosting, geolocation, etc)
    if decision.ip.is_hosting():
        # Requests from hosting IPs are likely from bots, so they can usually be
        # blocked. However, consider your use case - if this is an API endpoint
        # then hosting IPs might be legitimate.
        # https://docs.arcjet.com/blueprints/vpn-proxy-detection

        return JSONResponse(
            {"error": "Denied from hosting IP"},
            status_code=403,
        )

    return {"message": "Hello world", "decision": decision.to_dict()}

Flask

A synchronous example using Flask with the sync client.

# main.py
from flask import Flask, request, jsonify
import os

from arcjet import (
  arcjet_sync,
  shield,
  detect_bot,
  token_bucket,
  validate_email,
  is_spoofed_bot,
  Mode,
  BotCategory,
  EmailType,
)

app = Flask(__name__)

aj = arcjet_sync(
    key=os.environ["ARCJET_KEY"],
    rules=[
        shield(mode=Mode.LIVE),
        detect_bot(
            mode=Mode.LIVE, allow=[BotCategory.SEARCH_ENGINE, "OPENAI_CRAWLER_SEARCH"]
        ),
        token_bucket(mode=Mode.LIVE, refill_rate=5, interval=10, capacity=10),
        validate_email(
            mode=Mode.LIVE,
            deny=[EmailType.DISPOSABLE, EmailType.INVALID, EmailType.NO_MX_RECORDS],
        ),
    ],
)

@app.route("/")
def hello():
  # requested is optional; only relevant for token bucket rules (default: 1)
  # email is only required if validate_email() is configured
  decision = aj.protect(request, requested=1, email="example@arcjet.com")

  if decision.is_denied():
    status = 429 if decision.reason.is_rate_limit() else 403
    return jsonify(error="Denied", reason=decision.reason.to_dict()), status

  if decision.ip.is_hosting():
    return jsonify(error="Hosting IP blocked"), 403

  if any(is_spoofed_bot(r) for r in decision.results):
    return jsonify(error="Spoofed bot"), 403

  return jsonify(message="Hello world", decision=decision.to_dict())

if __name__ == "__main__":
  app.run(debug=True)

Custom characteristics

Each client is tracked by IP address by default. To customize client fingerprinting you can configure custom characteristics:

# main.py
from flask import Flask, request, jsonify
import os
import logging

from arcjet import (
    arcjet_sync,
    shield,
    detect_bot,
    token_bucket,
    Mode,
    BotCategory,
    EmailType,
)

app = Flask(__name__)

aj = arcjet_sync(
    key=os.environ["ARCJET_KEY"],  # Get your key from https://app.arcjet.com
    rules=[
        # Shield protects your app from common attacks e.g. SQL injection
        shield(mode=Mode.LIVE),
        # Create a bot detection rule
        detect_bot(
            mode=Mode.LIVE,
            allow=[
                BotCategory.SEARCH_ENGINE,  # Google, Bing, etc
                # Uncomment to allow these other common bot categories
                # See the full list at https://arcjet.com/bot-list
                # BotCategory.MONITOR", // Uptime monitoring services
                # BotCategory.PREVIEW", // Link previews e.g. Slack, Discord
            ],
        ),
        # Create a token bucket rate limit. Other algorithms are supported
        token_bucket(
            # Tracked by IP address by default, but this can be customized
            # See https://docs.arcjet.com/fingerprints
            # characteristics: ["ip.src"],
            mode=Mode.LIVE,
            refill_rate=5,  # Refill 5 tokens per interval
            interval=10,  # Refill every 10 seconds
            capacity=10,  # Bucket capacity of 10 tokens
        ),
    ],
)


@app.route("/")
def hello():
    # Call protect() to evaluate the request against the rules
    decision = aj.protect(
        request,
        requested=5,  # Deduct 5 tokens from the bucket
    )

    # Handle denied requests
    if decision.is_denied():
        status = 429 if decision.reason.is_rate_limit() else 403
        return jsonify(error="Denied", reason=decision.reason.to_dict()), status

    # Check IP metadata (VPNs, hosting, geolocation, etc)
    if decision.ip.is_hosting():
        # Requests from hosting IPs are likely from bots, so they can usually be
        # blocked. However, consider your use case - if this is an API endpoint
        # then hosting IPs might be legitimate.
        # https://docs.arcjet.com/blueprints/vpn-proxy-detection

        return jsonify(error="Hosting IP blocked"), 403

    return jsonify(message="Hello world", decision=decision.to_dict())


if __name__ == "__main__":
    app.run(debug=True)

Trusted proxies

When your app runs behind one or more reverse proxies or a load balancer, pass their IPs or CIDR ranges so Arcjet can correctly resolve the real client IP from X-Forwarded-For and similar headers.

from arcjet import arcjet

aj = arcjet(
    key=os.environ["ARCJET_KEY"],
    rules=[...],
    proxies=["10.0.0.0/8", "192.168.0.1"],
)

Only globally routable IPs are accepted for client identification; private, loopback, link-local, and addresses matching proxies are ignored during IP extraction.

Logging

Enable debug logging to troubleshoot issues with Arcjet integration.

import logging
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s %(levelname)s %(name)s %(message)s"
)

Arcjet logging can be controlled directly by setting the ARCJET_LOG_LEVEL environment variable e.g. export ARCJET_LOG_LEVEL=debug.

Support

This repository follows the Arcjet Support Policy.

Security

This repository follows the Arcjet Security Policy.

Compatibility

Packages maintained in this repository are compatible with Python 3.10 and above.

License

Licensed under the Apache License, Version 2.0.

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

arcjet-0.2.2.tar.gz (32.3 kB view details)

Uploaded Source

Built Distribution

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

arcjet-0.2.2-py3-none-any.whl (37.2 kB view details)

Uploaded Python 3

File details

Details for the file arcjet-0.2.2.tar.gz.

File metadata

  • Download URL: arcjet-0.2.2.tar.gz
  • Upload date:
  • Size: 32.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.24 {"installer":{"name":"uv","version":"0.9.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for arcjet-0.2.2.tar.gz
Algorithm Hash digest
SHA256 17383e0f23af00bf62bf5ca38515996e8a3452eaf0dc768c333de15c2cbb83ba
MD5 9c4916e1ffb7f5ba92e9752b5e727eb1
BLAKE2b-256 3523c4e121636fd8a2de0eafb057a02b20c7a499b43208bbedb52f8f9d9438a5

See more details on using hashes here.

File details

Details for the file arcjet-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: arcjet-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 37.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.24 {"installer":{"name":"uv","version":"0.9.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for arcjet-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 3a5c0905ff29217450ac122465b356f42d6e7e425facfdfaf3a4d14112ec9436
MD5 c9db0c827e3053a74c25393e2532a459
BLAKE2b-256 aeea0a23aed3393606ad7606c7cc6685a3851af0dda5a45bb818913901941d92

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