Skip to main content

LoginLlama Python Client

Official Python SDK for LoginLlama - AI-powered login security and fraud detection.

Features

  • Automatic Context Detection: Auto-detects IP address and User-Agent from Flask, Django, FastAPI, and other frameworks
  • Multi-Source IP Extraction: Supports X-Forwarded-For, CF-Connecting-IP, X-Real-IP, True-Client-IP with private IP filtering
  • Middleware Support: Drop-in middleware for Flask, Django, and FastAPI
  • Type Hints: Fully typed for excellent IDE support
  • Webhook Verification: Built-in HMAC signature verification

Installation

pip install loginllama

Or with uv:

uv pip install loginllama

Requires Python 3.11 or higher.

Quick Start

The simplest way to use LoginLlama is with the middleware pattern, which automatically captures request context:

from loginllama import LoginLlama
from flask import Flask, request, jsonify

app = Flask(__name__)
loginllama = LoginLlama(api_token='your-api-key')

# Add middleware to auto-capture request context
@app.before_request
def setup_loginllama():
    loginllama.middleware()()

@app.route('/login', methods=['POST'])
def login():
    try:
        # IP and User-Agent are automatically detected!
        result = loginllama.check(request.form['email'])

        if result.status == 'error' or result.risk_score > 5:
            print(f"Suspicious login blocked: {result.codes}")
            return jsonify({'error': 'Login blocked'}), 403

        # Continue with login...
        return jsonify({'success': True})
    except Exception as error:
        print(f'LoginLlama error: {error}')
        # Fail open on errors
        return jsonify({'success': True})

Without Middleware

If you prefer not to use middleware, you can pass the request explicitly:

result = loginllama.check(
    request.form['email'],
    request=request
)

Or provide IP and User-Agent manually:

result = loginllama.check(
    'user@example.com',
    ip_address='203.0.113.42',
    user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64)...'
)

Framework Examples

Flask

from flask import Flask, request, jsonify
from loginllama import LoginLlama

app = Flask(__name__)
loginllama = LoginLlama()  # Uses LOGINLLAMA_API_KEY env var

# Use middleware for automatic detection
@app.before_request
def setup_loginllama():
    loginllama.middleware()()

@app.route('/login', methods=['POST'])
def login():
    result = loginllama.check(
        request.form['email'],
        geo_country='US',
        geo_city='San Francisco'
    )

    if result.risk_score > 5:
        return jsonify({'error': 'Suspicious login'}), 403

    return jsonify({'success': True})

Django

from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from loginllama import LoginLlama

loginllama = LoginLlama()

@csrf_exempt
def login_view(request):
    if request.method == 'POST':
        email = request.POST.get('email')

        # Pass request explicitly for auto-detection
        result = loginllama.check(email, request=request)

        if result.risk_score > 5:
            return JsonResponse(
                {'error': 'Suspicious login'},
                status=403
            )

        return JsonResponse({'success': True})

    return JsonResponse({'error': 'Method not allowed'}, status=405)

FastAPI

from fastapi import FastAPI, Request, HTTPException
from loginllama import LoginLlama
from pydantic import BaseModel

app = FastAPI()
loginllama = LoginLlama()

class LoginRequest(BaseModel):
    email: str
    password: str

@app.post('/login')
async def login(login_data: LoginRequest, request: Request):
    # Pass request explicitly for auto-detection
    result = loginllama.check(
        login_data.email,
        request=request
    )

    if result.risk_score > 5:
        raise HTTPException(
            status_code=403,
            detail='Suspicious login detected'
        )

    return {'success': True}

API Reference

LoginLlama(api_token=None, base_url=None)

Create a new LoginLlama client.

Parameters:

  • api_token (optional): Your API key. Defaults to LOGINLLAMA_API_KEY environment variable
  • base_url (optional): Custom API endpoint for testing
from loginllama import LoginLlama

loginllama = LoginLlama(api_token='your-api-key')

loginllama.check(identity_key, **options)

Check a login attempt for suspicious activity.

Parameters:

  • identity_key (required): User identifier (email, username, user ID, etc.)
  • ip_address (optional): Override auto-detected IP address
  • user_agent (optional): Override auto-detected User-Agent
  • request (optional): Explicit request object (Flask, Django, FastAPI)
  • email_address (optional): User's email address for additional verification
  • geo_country (optional): ISO country code (e.g., 'US', 'GB')
  • geo_city (optional): City name for additional context
  • user_time_of_day (optional): Time of login attempt

Returns: LoginCheck object

class LoginCheck:
    status: str  # 'success' or 'error'
    message: str
    codes: List[LoginCheckStatus]
    risk_score: int  # 0-10 scale
    environment: str
    meta: Optional[dict]

Detection Priority:

  1. Explicit ip_address and user_agent keyword arguments
  2. Extract from request object if provided
  3. Use context from middleware (if used)

Examples:

# Auto-detect from middleware context
result = loginllama.check('user@example.com')

# Pass request explicitly
result = loginllama.check('user@example.com', request=request)

# Manual override
result = loginllama.check(
    'user@example.com',
    ip_address='203.0.113.42',
    user_agent='Mozilla/5.0...'
)

# With additional context
result = loginllama.check(
    'user@example.com',
    email_address='user@example.com',
    geo_country='US',
    geo_city='San Francisco'
)

loginllama.middleware()

Returns middleware function that automatically captures request context using contextvars.

Flask:

@app.before_request
def setup_loginllama():
    loginllama.middleware()()

Django (middleware class):

class LoginLlamaMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.loginllama = LoginLlama()

    def __call__(self, request):
        self.loginllama.middleware()(request)
        return self.get_response(request)

FastAPI:

@app.middleware("http")
async def loginllama_middleware(request: Request, call_next):
    loginllama.middleware()(request)
    response = await call_next(request)
    return response

verify_webhook_signature(payload, signature, secret)

Verify webhook signature using constant-time HMAC comparison.

Parameters:

  • payload: Raw webhook body (bytes or str)
  • signature: Value from X-LoginLlama-Signature header
  • secret: Webhook secret from LoginLlama dashboard

Returns: bool

from loginllama import verify_webhook_signature
from flask import Flask, request

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    payload = request.get_data()
    signature = request.headers.get('X-LoginLlama-Signature')

    if not verify_webhook_signature(payload, signature, os.environ['WEBHOOK_SECRET']):
        return 'Invalid signature', 401

    event = request.get_json()
    # Handle event...
    return 'ok', 200

Login Status Codes

The SDK exports a LoginCheckStatus enum with all possible status codes:

from loginllama import LoginCheckStatus

# Example status codes:
LoginCheckStatus.VALID
LoginCheckStatus.IP_ADDRESS_SUSPICIOUS
LoginCheckStatus.KNOWN_BOT
LoginCheckStatus.IMPOSSIBLE_TRAVEL_DETECTED
LoginCheckStatus.NEW_LOGIN_LOCATION
# ... and more

Error Handling

The SDK will raise exceptions if required parameters are missing:

try:
    result = loginllama.check('user@example.com')
except ValueError as error:
    if 'IP address could not be detected' in str(error):
        # No IP available - pass ip_address or request explicitly
        # or use middleware()
        pass
except Exception as error:
    # Consider failing open on errors to avoid blocking legitimate users
    print(f'LoginLlama error: {error}')

Best Practice: Fail open on errors to avoid blocking legitimate users during API outages:

try:
    result = loginllama.check(email)
    if result.risk_score > 5:
        # Block suspicious login
        return jsonify({'error': 'Login blocked'}), 403
except Exception as error:
    print(f'LoginLlama error: {error}')
    # Fail open - allow login to proceed
    return jsonify({'success': True})

IP Detection

The SDK automatically detects IP addresses from multiple sources with priority fallback:

  1. X-Forwarded-For - Parses chain, takes first public IP (filters private IPs)
  2. CF-Connecting-IP - Cloudflare real client IP
  3. X-Real-IP - nginx proxy header
  4. True-Client-IP - Akamai/Cloudflare header
  5. Direct connection - REMOTE_ADDR, framework-specific attributes

Private IP Filtering: Automatically filters 10.x.x.x, 172.16-31.x.x, 192.168.x.x, 127.x.x.x, ::1, fc00::/7, fe80::/10

Type Hints

The SDK is fully typed with type hints:

from loginllama import (
    LoginLlama,
    LoginCheck,
    LoginCheckStatus,
    verify_webhook_signature
)

Contributing

Contributions are welcome! Please open an issue or submit a pull request on GitHub.

License

MIT License

Support

Metadata

Release files for loginllama 3.0.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 loginllama 3.0.0
File Size Uploaded Provenance
loginllama-3.0.0.tar.gz 25.7 kB With provenance Details

Built distribution (wheel)

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

Total release size: 49.5 kB

Release files / loginllama-3.0.0.tar.gz

Download URL loginllama-3.0.0.tar.gz
Size 25.7 kB
Tags Source
SHA-256 checksum
How to use checksums
1fbea12339ff1444e9f1ca38779f6953b4ba4905e5914626837140e70989a02e
BLAKE2b-256 checksum
How to use checksums
9c9ab9cd9c1ae615528aa9f125ba9867e497b94dd121544d13d79b5a69957b4e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / loginllama-3.0.0-py3-none-any.whl

Download URL loginllama-3.0.0-py3-none-any.whl
Size 23.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1a17a3742b9a00827c0cd501d0362ca6d6194570e7e3ff9302aa1a7454e18877
BLAKE2b-256 checksum
How to use checksums
9d36e7ff7f042755bca6b9461731b2b5a10697b81e3c59b4de9a63576aab90ec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Security for loginllama 3.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for loginllama-3.0.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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