Skip to main content

Flask Integration

Start accepting x402 payments in your Flask server in 2 minutes

Share
Last updated on October 5, 2026
On this page

Start accepting x402 payments in your Flask server in 2 minutes.

Example Code

You can find the full code for this example on GitHub.

Step 1: Install Dependencies​

Install the required packages for your Flask server:

bash
pip install x402 flask python-dotenv

Step 2: Set Your Environment Variables​

Create a .env file in your project root:

bash
echo "ADDRESS=0x...\nNETWORK=sui" > .env

Your .env file should look like this:

env
ADDRESS=0x... # wallet public address you want to receive payments to
NETWORK=sui # recommended for fastest settlement
# Optional: Configure facilitator URL if using custom facilitator
# FACILITATOR_URL=https://x402.bex.co
Network Options

bex.co supports multiple networks:

  • sui (recommended for fastest settlement)
  • ethereum
  • base
  • polygon
  • avalanche

Step 3: Create a New Flask App​

Create an app.py file with the following code:

python
import os
from typing import Any, Dict
 
from dotenv import load_dotenv
from flask import Flask, jsonify
from x402.flask.middleware import PaymentMiddleware
from x402.types import TokenAmount, TokenAsset, EIP712Domain
 
# Load environment variables
load_dotenv()
 
# Get configuration from environment
ADDRESS = os.getenv("ADDRESS")
NETWORK = os.getenv("NETWORK", "sui")
 
if not ADDRESS:
    raise ValueError("Missing required environment variables")
 
app = Flask(__name__)
 
# Initialize payment middleware
payment_middleware = PaymentMiddleware(app)
 
# Apply payment middleware to specific routes
payment_middleware.add(
    path="/weather",
    price="$0.001",
    pay_to_address=ADDRESS,
    network=NETWORK,
)
 
# Apply payment middleware to premium routes
payment_middleware.add(
    path="/premium/*",
    price=TokenAmount(
        amount="10000",
        asset=TokenAsset(
            address="0x...",  # USDC contract address for your network
            decimals=6,
            eip712=EIP712Domain(name="USDC", version="2"),
        ),
    ),
    pay_to_address=ADDRESS,
    network=NETWORK,
)
 
 
@app.route("/")
def index() -> Dict[str, str]:
    """Root endpoint"""
    return jsonify({
        "message": "x402 Payment Server",
        "status": "running",
    })
 
 
@app.route("/weather")
def get_weather() -> Dict[str, Any]:
    """Get weather data - requires payment"""
    return jsonify({
        "report": {
            "weather": "sunny",
            "temperature": 70,
            "location": "San Francisco",
        }
    })
 
 
@app.route("/premium/content")
def get_premium_content() -> Dict[str, Any]:
    """Get premium content - requires payment"""
    return jsonify({
        "content": "This is premium content",
        "type": "article",
        "timestamp": "2025-01-01T00:00:00Z",
    })
 
 
@app.route("/premium/analytics")
def get_premium_analytics() -> Dict[str, Any]:
    """Get premium analytics - requires payment"""
    return jsonify({
        "analytics": {
            "users": 1000,
            "revenue": 50000,
            "growth": 25.5,
        }
    })
 
 
if __name__ == "__main__":
    app.run(
        host="0.0.0.0",
        port=4021,
        debug=True,
    )

Step 4: Run the Server​

Start your Flask server:

bash
python app.py

Or use Flask's built-in development server:

bash
flask --app app run --port 4021
Your Flask server is now accepting x402 payments!

Your server will be available at http://localhost:4021.

Step 5: Test the Server​

You can test payments against your server locally using HTTP clients like curl, Postman, or by building a Python client application.

Coming Soon

Client implementation guides for httpx and requests libraries will be available soon.

Manual Testing with curl​

bash
# This will return a 402 Payment Required response
curl http://localhost:4021/weather
 
# The response will include payment details in headers

Payment Configuration Options​

The PaymentMiddleware.add() method accepts flexible payment configurations:

Simple Dollar Amount​

python
# Initialize payment middleware
payment_middleware = PaymentMiddleware(app)
 
payment_middleware.add(
    path="/weather",
    price="$0.001",
    pay_to_address=ADDRESS,
    network="sui",
)
 
@app.route("/weather")
def get_weather():
    return jsonify({"weather": "sunny"})

Token Amount with Specific Asset​

python
from x402.types import TokenAmount, TokenAsset, EIP712Domain
 
payment_middleware.add(
    path="/premium/data",
    price=TokenAmount(
        amount="10000",
        asset=TokenAsset(
            address="0x...",  # Token contract address
            decimals=6,
            eip712=EIP712Domain(name="USDC", version="2"),
        ),
    ),
    pay_to_address=ADDRESS,
    network="sui",
)
 
@app.route("/premium/data")
def get_premium_data():
    return jsonify({"data": "Premium content"})

Multiple Routes with Same Payment​

You can protect multiple routes with wildcard paths:

python
# Protect all /premium routes with the same payment requirement
payment_middleware.add(
    path="/premium/*",
    price="$0.05",
    pay_to_address=ADDRESS,
    network="sui",
)
 
@app.route("/premium/content")
def premium_content():
    return jsonify({"content": "Premium content"})
 
@app.route("/premium/analytics")
def premium_analytics():
    return jsonify({"analytics": {...}})

Advanced Features​

Custom Payment Verification Callback​

Add custom logic when payments are verified:

python
# Note: Custom payment verification callbacks may vary based on
# the x402 library version. Check the official documentation for
# the exact callback mechanism supported by your version.
 
payment_middleware.add(
    path="/weather",
    price="$0.001",
    pay_to_address=ADDRESS,
    network="sui",
)
 
@app.route("/weather")
def get_weather():
    # Payment has been verified if this handler is reached
    # Add your custom logic here
    return jsonify({"weather": "sunny"})

Error Handling​

Add custom error handling for payment failures:

python
from flask import request
from x402.exceptions import X402PaymentError
 
@app.errorhandler(X402PaymentError)
def handle_payment_error(error):
    return jsonify({
        "error": "Payment required",
        "details": str(error),
        "path": request.path,
    }), 402
 
@app.errorhandler(Exception)
def handle_general_error(error):
    app.logger.error(f"Unexpected error: {error}")
    return jsonify({
        "error": "Internal server error",
    }), 500

CORS Configuration​

Enable CORS for cross-origin requests:

python
from flask_cors import CORS
 
# Enable CORS for all routes
CORS(app, resources={
    r"/*": {
        "origins": ["http://localhost:3000", "https://yourdomain.com"],
        "allow_headers": ["Content-Type", "Authorization"],
        "methods": ["GET", "POST", "OPTIONS"],
    }
})

Install flask-cors:

bash
pip install flask-cors

Request Context Access​

Access payment information in your route handlers:

python
from flask import g
 
@app.route("/weather")
@require_payment(
    price="$0.001",
    pay_to_address=ADDRESS,
    network="sui",
    facilitator_config=facilitator_config,
)
def get_weather():
    # Access payment details from Flask's g object
    payment_info = getattr(g, "x402_payment", None)
 
    return jsonify({
        "report": {"weather": "sunny", "temperature": 70},
        "payment": {
            "tx_hash": payment_info.tx_hash if payment_info else None,
        }
    })

Logging Configuration​

Set up proper logging for production:

python
import logging
from logging.handlers import RotatingFileHandler
 
if not app.debug:
    # Create logs directory if it doesn't exist
    if not os.path.exists('logs'):
        os.mkdir('logs')
 
    # Configure file handler
    file_handler = RotatingFileHandler(
        'logs/x402_server.log',
        maxBytes=10240000,
        backupCount=10
    )
    file_handler.setFormatter(logging.Formatter(
        '%(asctime)s %(levelname)s: %(message)s '
        '[in %(pathname)s:%(lineno)d]'
    ))
    file_handler.setLevel(logging.INFO)
    app.logger.addHandler(file_handler)
 
    app.logger.setLevel(logging.INFO)
    app.logger.info('x402 payment server startup')

Production Deployment​

Before deploying to production:

  1. Switch to production network (e.g., sui instead of sui-testnet)
  2. Use environment variables for all sensitive data
  3. Disable debug mode (debug=False)
  4. Use a production WSGI server (Gunicorn, uWSGI)
  5. Enable HTTPS (use a reverse proxy like nginx)
  6. Set up proper logging and monitoring
  7. Configure CORS for your production domains

Production Example​

python
import os
import logging
from logging.handlers import RotatingFileHandler
 
from dotenv import load_dotenv
from flask import Flask, jsonify
from flask_cors import CORS
from x402.flask.middleware import PaymentMiddleware
 
load_dotenv()
 
app = Flask(__name__)
app.config['DEBUG'] = False
 
# CORS configuration
CORS(app, resources={
    r"/*": {
        "origins": os.getenv("ALLOWED_ORIGINS", "").split(","),
        "allow_headers": ["Content-Type", "Authorization"],
        "methods": ["GET", "POST", "OPTIONS"],
    }
})
 
# Logging configuration
if not app.debug:
    if not os.path.exists('logs'):
        os.mkdir('logs')
    file_handler = RotatingFileHandler(
        'logs/x402_server.log',
        maxBytes=10240000,
        backupCount=10
    )
    file_handler.setFormatter(logging.Formatter(
        '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
    ))
    file_handler.setLevel(logging.INFO)
    app.logger.addHandler(file_handler)
    app.logger.setLevel(logging.INFO)
 
# Initialize payment middleware
payment_middleware = PaymentMiddleware(app)
 
payment_middleware.add(
    path="/api/data",
    price="$0.01",
    pay_to_address=os.getenv("ADDRESS"),
    network="sui",  # Production Sui network
)
 
@app.route("/api/data")
def get_data():
    app.logger.info("Delivering protected data")
    return jsonify({"data": "Protected content"})
 
if __name__ == "__main__":
    app.run(
        host="0.0.0.0",
        port=int(os.getenv("PORT", "4021")),
        debug=False,
    )

Deploy with Gunicorn​

For production, use Gunicorn:

bash
pip install gunicorn
 
gunicorn app:app \
  --workers 4 \
  --bind 0.0.0.0:4021 \
  --timeout 120 \
  --access-logfile logs/access.log \
  --error-logfile logs/error.log

Deploy with uWSGI​

Alternatively, use uWSGI:

bash
pip install uwsgi
 
uwsgi --http 0.0.0.0:4021 \
  --wsgi-file app.py \
  --callable app \
  --processes 4 \
  --threads 2

Troubleshooting​

Common Issues​

Payment verification fails

  • Verify your BEX_API_KEY is correct
  • Check that the wallet address format matches the network
  • Ensure the facilitator URL is accessible

CORS errors

  • Install and configure flask-cors
  • Ensure CORS middleware is configured before routes
  • Check allowed origins configuration

Network mismatch

  • Ensure client and server use the same network
  • Check that the token address is valid for the network

Module import errors

  • Ensure you installed x402 package
  • Verify your Python version is 3.8 or higher

Decorator order matters

  • @require_payment should be the closest decorator to the function
  • Example correct order:
    python
    @app.route("/weather")
    @require_payment(...)
    def get_weather():
        ...

Port already in use

  • Change the port in app.run() configuration
  • Kill any existing processes: lsof -ti:4021 | xargs kill

Why Choose Flask?​

Flask is an excellent choice for x402 payment servers:

  • Lightweight: Minimal and unopinionated framework
  • Flexible: Easy to extend with plugins
  • Easy to learn: Simple, intuitive API
  • Large community: Extensive documentation and third-party extensions
  • Production-ready: Battle-tested in thousands of applications

Flask vs FastAPI​

FeatureFlaskFastAPI
PerformanceGoodExcellent
Async SupportVia extensionsBuilt-in
Type SafetyOptionalBuilt-in
Auto DocsVia extensionsBuilt-in
Learning CurveEasyModerate
MaturityVery matureModern

Choose Flask if you:

  • Want a simple, familiar framework
  • Don't need async features
  • Prefer flexibility over structure
  • Are building a traditional web app

Choose FastAPI if you:

  • Need high performance
  • Want built-in async support
  • Prefer type safety
  • Want automatic API documentation

Next Steps​

Need Help?​

Join Our Community

Have questions or want to connect with other developers?

Join Discord

Was this page helpful?

One API key for 30+ chains

bex router is a single multi-chain gateway — one key, one bill, metered in compute units instead of an account per network.

Explore bex router