---
title: Flask Integration
description: Start accepting x402 payments in your Flask server in 2 minutes
keywords: [x402, Flask, Python, blockchain payments, API monetization]
last_updated: 2026-08-20
---

# Getting Started with Flask

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

:::info Example Code
You can find the full code for this example on [GitHub](https://github.com/BlockEdenHQ/x402-examples/tree/main/python/servers/flask).
:::

## 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
```

:::tip 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
```

<div style={{padding: '1rem', background: '#d1fae5', borderRadius: '0.5rem', color: '#065f46', marginTop: '1rem', marginBottom: '1rem'}}>
  <ion-icon name="checkmark-circle" style={{fontSize: '1.5rem', verticalAlign: 'middle', marginRight: '0.5rem'}}></ion-icon>
  <strong>Your Flask server is now accepting x402 payments!</strong>
</div>

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.

:::note 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:

- <ion-icon name="cube"></ion-icon> **Lightweight**: Minimal and unopinionated framework
- <ion-icon name="construct"></ion-icon> **Flexible**: Easy to extend with plugins
- <ion-icon name="school"></ion-icon> **Easy to learn**: Simple, intuitive API
- <ion-icon name="people"></ion-icon> **Large community**: Extensive documentation and third-party extensions
- **Production-ready**: Battle-tested in thousands of applications

## Flask vs FastAPI

| Feature            | Flask          | FastAPI   |
| ------------------ | -------------- | --------- |
| **Performance**    | Good           | Excellent |
| **Async Support**  | Via extensions | Built-in  |
| **Type Safety**    | Optional       | Built-in  |
| **Auto Docs**      | Via extensions | Built-in  |
| **Learning Curve** | Easy           | Moderate  |
| **Maturity**       | Very mature    | Modern    |

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](./x402-fastapi.md)** if you:

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

## Next Steps

- <ion-icon name="arrow-forward-circle"></ion-icon> [Implement a crypto paywall](./implement-a-crypto-paywall-with-x402.md)
- <ion-icon name="arrow-forward-circle"></ion-icon> [View the merchant introduction](./x402-for-merchants.md)
- <ion-icon name="arrow-forward-circle"></ion-icon> [Read the Quick Start Guide](./quickstart.md)

## Need Help?

<div style={{padding: '1.5rem', background: '#f3f4f6', borderRadius: '0.5rem', marginTop: '2rem'}}>
  <div style={{display: 'flex', alignItems: 'flex-start', marginBottom: '1rem'}}>
    <ion-icon name="people" style={{fontSize: '1.5rem', marginRight: '0.5rem', color: '#374151'}}></ion-icon>
    <div>
      <h4 style={{margin: 0, fontSize: '1.125rem', fontWeight: 600, color: '#111827'}}>Join Our Community</h4>
      <p style={{margin: '0.25rem 0 0 0', color: '#6b7280'}}>Have questions or want to connect with other developers?</p>
    </div>
  </div>
  <a
    href="https://discord.gg/4Yfvs2HWey"
    target="_blank"
    rel="noopener noreferrer"
    style={{
      display: 'inline-flex',
      alignItems: 'center',
      gap: '0.5rem',
      padding: '0.625rem 1.25rem',
      background: '#5865F2',
      color: 'white',
      borderRadius: '0.5rem',
      textDecoration: 'none',
      fontWeight: 600,
      fontSize: '0.9375rem',
      transition: 'background 0.2s',
      border: 'none'
    }}
    onMouseOver={(e) => e.currentTarget.style.background = '#4752C4'}
    onMouseOut={(e) => e.currentTarget.style.background = '#5865F2'}
  >
    <ion-icon name="logo-discord" style={{fontSize: '1.25rem'}}></ion-icon>
    <span>Join Discord</span>
  </a>
</div>
