Skip to main content
Build Status

The official Python SDK for Castle. Castle analyzes user behavior in web and mobile apps to stop fraud before it happens.

This package is a thin wrapper around the Castle HTTP API. It exposes risk assessment, event logging, Lists, Privacy (GDPR), Events (enterprise), and webhook verification. See the API reference for supported events and payload shapes.

Requirements

  • Python 3.9 or newer

  • A Castle API secret

Installation

pip install castle

Quick start

import os
from castle.configuration import configuration
from castle.client import Client

configuration.api_secret = os.environ['CASTLE_API_SECRET']

client = Client.from_request(request)
verdict = client.risk({
    'event': '$login',
    'status': '$succeeded',
    'request_token': request.POST.get('castle_request_token'),
    'user': {'id': '12345', 'email': 'user@example.com'},
})

action = verdict.get('policy', {}).get('action') or verdict.get('action')
if action == 'deny':
    # block the user
    pass
elif action == 'challenge':
    # send 2FA / additional verification
    pass
else:
    # allow
    pass

Client.from_request builds request context (IP, headers, client id) from a framework request object. See Advanced configuration for header allow/deny lists and proxy chains.

Configuration

The minimal, recommended setup:

import os
from castle.configuration import configuration

configuration.api_secret = os.environ['CASTLE_API_SECRET']

# Behavior when Castle's API is unreachable or returns a 5xx.
# One of: allow (default), deny, challenge, throw
configuration.failover_strategy = 'allow'

# Request timeout in milliseconds (default: 1000).
# RequestError is raised on timeout.
configuration.request_timeout = 1000

Logging

import logging
from castle.configuration import configuration

configuration.logger = logging.getLogger('castle')

The logger only needs to respond to info. Each request and response is logged with sensitive values stripped.

Multi-environment / multi-tenant

Most apps only need the global configuration singleton, but you can also create standalone Configuration instances and pass them per call via APIRequest:

from castle.configuration import Configuration
from castle.api_request import APIRequest
from castle.commands.risk import CommandsRisk

config = Configuration()
config.api_secret = os.environ['CASTLE_API_SECRET_TENANT_A']

APIRequest(config).call(CommandsRisk(context).call({
    'event': '$login',
    'status': '$succeeded',
    'request_token': '<token>',
    'user': {'id': '1234'},
}))

Usage

See Castle documentation and the API reference for endpoint details, event types, and integration guides.

Advanced configuration

The defaults work for most deployments. The options below only matter if you have a non-trivial proxy chain or strict header policies.

Header allow/deny lists

By default the SDK sends every HTTP header except Cookie and Authorization. Castle uses these headers to fingerprint the request.

from castle.configuration import configuration, DEFAULT_ALLOWLIST

# Always-blocked headers (in addition to Cookie/Authorization).
configuration.denylisted = ['HTTP-X-Internal-Header']

# Strict allow-list mode. Headers outside the list are scrubbed,
# except User-Agent which is always preserved.
configuration.allowlisted = DEFAULT_ALLOWLIST

Header names are case-insensitive and accept both _ and - as separators. A leading HTTP_ prefix is stripped automatically.

Client IP detection

Castle needs the original client IP, not the IP of your proxy or load balancer. The SDK reads X-Forwarded-For and Remote-Addr by default; pick one of the strategies below:

from castle.configuration import configuration, TRUSTED_PROXIES

# 1. Custom header (e.g. Cloudflare's Cf-Connecting-Ip).
configuration.ip_headers = ['Cf-Connecting-Ip']

# 2. Static, known proxy IPs (strings or regexes).
configuration.trusted_proxies = ['10.0.0.1']

# 3. Ephemeral proxies but known chain depth.
configuration.trusted_proxy_depth = 2

# 4. Last resort: trust the entire X-Forwarded-For chain.
# Warning: vulnerable to header spoofing if a malicious proxy is in path.
configuration.trust_proxy_chain = False

Use either trusted_proxies or trusted_proxy_depth, not both. Private/loopback ranges in TRUSTED_PROXIES are always considered trusted.

Optional settings

from castle.configuration import configuration

# Override the API base URL (default: https://api.castle.io/v1)
# configuration.base_url = 'https://api.castle.io/v1'

Signature

Secure mode signs user identifiers on the server:

from castle.secure_mode import signature

signature(user_id)

Exceptions

All exceptions inherit from CastleError. The most useful ones:

  • ConfigurationError — the SDK is misconfigured (missing API secret, invalid URL, etc.)

  • RequestError — network failure or timeout reaching Castle

  • InvalidRequestTokenError — the request token is missing or invalid

  • InvalidParametersError — 422 response with validation details

  • RateLimitError — 429 response; back off and retry

  • PaymentRequiredError — 402 response

  • UnauthorizedError — 401; bad API secret

  • InternalServerError — 5xx response from Castle

  • WebhookVerificationError — webhook signature did not match

The full list is in castle/errors.py.

Release files for castle 7.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for castle 7.3.0
File Size Uploaded
castle-7.3.0.tar.gz 19.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for castle 7.3.0
File Interpreter ABI Platform
castle-7.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 50.0 kB

Release files / castle-7.3.0.tar.gz

Download URL castle-7.3.0.tar.gz
Size 19.3 kB
Tags Source
SHA-256 checksum
How to use checksums
0aa0741b5ad40f791e60a9705e498b34cfbfe169ba4a65d51b79eefcb1ce62a6
BLAKE2b-256 checksum
How to use checksums
ab79ae57371a56b1d549de3172b464db1f51397b0ba2392199d226fe03b9ee41
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.3

Release files / castle-7.3.0-py3-none-any.whl

Download URL castle-7.3.0-py3-none-any.whl
Size 30.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a026ad0a49f19625034aeedabdfd74134bba61f4b39393c00e46f7ba520af6fa
BLAKE2b-256 checksum
How to use checksums
d6a50be08e7d3343c5091c42eb2b9d529689c8551c5a0d846c4315b66125eec0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.3

Release history Release notifications | RSS feed

This release

7.3.0 This release

2 release files

7.2.0

2 release files

7.1.0

2 release files

7.0.0

2 release files

6.1.0

2 release files

6.0.0

2 release files

5.0.1

2 release files

5.0.0

2 release files

4.0.0

2 release files

3.3.0

2 release files

3.2.0

2 release files

3.1.0

2 release files

3.0.0

2 release files

2.4.0

2 release files

2.3.1

2 release files

2.3.0

2 release files

2.2.1

2 release files

2.2.0

2 release files

2.1.0

2 release files

2.0.0

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.0.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page