Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.swiftpay.finance/llms.txt

Use this file to discover all available pages before exploring further.

The swiftpay-x402-fastapi-guard middleware adds HTTP 402 payment requirements to your FastAPI routes. Protect specific endpoints with per-request stablecoin payments.

Features

  • Two-round payment flow — Return 402 + requirements, then serve on second request with payment
  • FastAPI middleware — Integrates seamlessly with FastAPI’s middleware stack
  • Server-agnostic signing — SwiftPay handles EIP-712 signature verification
  • No key management — Your server never touches private keys
  • Type-safe — Full type hints with Python 3.10+

Installation

pip install swiftpay-x402-fastapi-guard
Also requires swiftpay-api-client:
pip install swiftpay-api-client

Quick Start

from fastapi import FastAPI
from swiftpay import AsyncSwiftPay
from swiftpay_x402_fastapi_guard import X402Guard

app = FastAPI()

client = AsyncSwiftPay(secret_key="sk_live_...")

# Register middleware
app.add_middleware(
    X402Guard,
    client=client,
    routes={"/v1/analyze": "https://api.example.com/v1/analyze"},
)

@app.get("/v1/analyze")
async def analyze():
    return {"sentiment": "positive", "score": 0.87}

How It Works

The x402 payment flow is a two-round process:

Round 1: No Payment Header

Client makes a request without the X-PAYMENT header:
GET /v1/analyze
Server responds with HTTP 402 and payment requirements:
HTTP/1.1 402 Payment Required

{
  "x402-version": "v1",
  "x402-nonce": "...",
  "x402-payment-requirements": {
    "asset": "USDC",
    "network": "ethereum",
    "amount": "0.05",
    ...
  }
}

Round 2: With Payment Proof

Client signs the payment requirements and resends the request with the signed proof:
GET /v1/analyze
X-PAYMENT: <signed-eip712-payload>
Server validates the signature via SwiftPay’s API and serves the resource:
HTTP/1.1 200 OK

{
  "sentiment": "positive",
  "score": 0.87
}

Configuration

X402Guard(
    client: AsyncSwiftPay,                        # Required: SwiftPay API client
    routes: dict[str, str] = {},                  # Route path → endpoint URL mapping
    # Optional:
    base_url: str = "https://api.swiftpay.finance",  # API base URL
    timeout: float = 30.0,                        # Request timeout in seconds
)
Routes mapping:
  • Key: The FastAPI route path (e.g., /v1/analyze)
  • Value: The endpoint URL registered with SwiftPay
  • Routes not in this mapping pass through unprotected

Usage Patterns

Protect Specific Routes

from fastapi import FastAPI
from swiftpay import AsyncSwiftPay
from swiftpay_x402_fastapi_guard import X402Guard

app = FastAPI()
client = AsyncSwiftPay(secret_key="sk_live_...")

app.add_middleware(
    X402Guard,
    client=client,
    routes={
        "/v1/analyze": "https://api.example.com/v1/analyze",
        "/v1/translate": "https://api.example.com/v1/translate",
    },
)

@app.get("/v1/analyze")
async def analyze(text: str):
    return {"sentiment": "positive", "score": 0.87}

@app.get("/v1/translate")
async def translate(text: str, target: str):
    return {"translated": "translated text"}

@app.get("/v1/public")
async def public_endpoint():
    # Not in routes → no payment required
    return {"public": "data"}

Tiered Pricing

from enum import Enum

class Tier(str, Enum):
    BASIC = "basic"
    PREMIUM = "premium"

app.add_middleware(
    X402Guard,
    client=client,
    routes={
        "/v1/analyze-basic": "https://api.example.com/analyze-basic",      # Lower cost
        "/v1/analyze-premium": "https://api.example.com/analyze-premium",  # Higher cost
    },
)

@app.get("/v1/analyze/{tier}")
async def analyze(tier: Tier, text: str):
    if tier == Tier.PREMIUM:
        return {"analysis": "detailed results"}
    return {"analysis": "basic results"}

Environment-Based Configuration

import os
from dotenv import load_dotenv

load_dotenv()

client = AsyncSwiftPay(
    secret_key=os.getenv("SWIFTPAY_SECRET_KEY"),
    base_url=os.getenv("SWIFTPAY_API_URL", "https://api.swiftpay.finance"),
)

# Production: All routes require payment
# Development: Routes pass through unprotected
routes = {
    "/v1/analyze": "https://api.example.com/v1/analyze",
    "/v1/translate": "https://api.example.com/v1/translate",
} if os.getenv("ENV") == "production" else {}

app.add_middleware(X402Guard, client=client, routes=routes)

Error Handling

The middleware handles payment errors automatically. Additional route-level error handling:
from fastapi import FastAPI, HTTPException
from swiftpay.errors import SwiftPayError

@app.exception_handler(SwiftPayError)
async def swiftpay_error_handler(request, exc):
    return JSONResponse(
        status_code=402,
        content={
            "error": "payment_required",
            "message": str(exc),
        },
    )

@app.get("/v1/analyze")
async def analyze(text: str):
    try:
        result = perform_analysis(text)
        return {"sentiment": result.sentiment}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

Client Implementation

Your frontend clients need to handle the 402 response and submit payment:
async function makePaymentRequest(url: string, options?: RequestInit) {
  // First request
  let response = await fetch(url, options);
  
  if (response.status === 402) {
    // Extract payment requirements
    const requirements = response.headers.get('x402-payment-requirements');
    
    // User signs payment proof (using MetaMask, etc)
    const signature = await window.ethereum.request({
      method: 'eth_signTypedData_v4',
      params: [userAddress, JSON.stringify(JSON.parse(requirements))],
    });
    
    // Second request with payment proof
    response = await fetch(url, {
      ...options,
      headers: {
        ...options?.headers,
        'X-PAYMENT': signature,
      },
    });
  }
  
  return response;
}

Full API Reference

For complete configuration options, advanced error handling, and additional patterns, see the official x402 FastAPI Guard on PyPI. All APIs are in v0.1.0-beta — stable and production-ready.