This guide walks you through integrating with x402 to enable payments for your API or service. By the end, your API will be able to charge buyers and AI agents for access.
This quickstart begins with testnet configuration for safe testing. When you’re ready for production, see Running on Mainnet for the simple changes needed to accept real payments.
Prerequisites
Before you begin, ensure you have:
- A crypto wallet to receive funds (any EVM-compatible wallet, e.g., CDP Wallet)
- (Optional) A Coinbase Developer Platform (CDP) account and API Keys
- Required for mainnet use until other facilitators go live
- Node.js and npm, or Python and pip installed
- An existing API or server
We have pre-configured examples available in our repo for both Node.js and Python. We also have an advanced example that shows how to use the x402 SDKs to build a more complex payment flow.
1. Install Dependencies
The mainnet facilitator packages (@coinbase/x402
for Node.js, cdp
for Python) are only needed for production. For testnet development, you can skip these. See Running on Mainnet for details.
2. Add Payment Middleware
Integrate the payment middleware into your application. You will need to provide:
- The Facilitator URL or facilitator object. For testing, use
https://x402.org/facilitator
which works on Base Sepolia.
- The routes you want to protect
- Your receiving wallet address
The examples below show testnet configuration. When you’re ready to accept real payments, refer to Running on Mainnet for the simple changes needed.
Full example in the repo here.
import express from "express";
import { paymentMiddleware, Network } from "x402-express";
// import { facilitator } from "@coinbase/x402"; // For mainnet
const app = express();
app.use(paymentMiddleware(
"0xYourAddress", // your receiving wallet address
{ // Route configurations for protected endpoints
"GET /weather": {
// USDC amount in dollars
price: "$0.001",
network: "base-sepolia", // for mainnet, see Running on Mainnet section
},
},
{
url: "https://x402.org/facilitator", // for testnet
}
));
// Implement your route
app.get("/weather", (req, res) => {
res.send({
report: {
weather: "sunny",
temperature: 70,
},
});
});
app.listen(4021, () => {
console.log(`Server listening at http://localhost:4021`);
});
Full example in the repo here.
import express from "express";
import { paymentMiddleware, Network } from "x402-express";
// import { facilitator } from "@coinbase/x402"; // For mainnet
const app = express();
app.use(paymentMiddleware(
"0xYourAddress", // your receiving wallet address
{ // Route configurations for protected endpoints
"GET /weather": {
// USDC amount in dollars
price: "$0.001",
network: "base-sepolia", // for mainnet, see Running on Mainnet section
},
},
{
url: "https://x402.org/facilitator", // for testnet
}
));
// Implement your route
app.get("/weather", (req, res) => {
res.send({
report: {
weather: "sunny",
temperature: 70,
},
});
});
app.listen(4021, () => {
console.log(`Server listening at http://localhost:4021`);
});
Full example in the repo here.
import { paymentMiddleware, Network } from 'x402-next';
// import { facilitator } from "@coinbase/x402"; // For mainnet
// Configure the payment middleware
export const middleware = paymentMiddleware(
"0xYourAddress", // your receiving wallet address
{ // Route configurations for protected endpoints
'/protected': {
price: '$0.01',
network: "base-sepolia", // for mainnet, see Running on Mainnet section
config: {
description: 'Access to protected content'
}
},
},
{
url: "https://x402.org/facilitator", // for testnet
}
);
// Configure which paths the middleware should run on
export const config = {
matcher: [
'/protected/:path*',
]
};
Full example in the repo here.
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { paymentMiddleware, Network } from "x402-hono";
// import { facilitator } from "@coinbase/x402"; // For mainnet
const app = new Hono();
// Configure the payment middleware
app.use(paymentMiddleware(
"0xYourAddress", // your receiving wallet address
{ // Route configurations for protected endpoints
"/protected-route": {
price: "$0.10",
network: "base-sepolia", // for mainnet, see Running on Mainnet section
config: {
description: "Access to premium content",
}
}
},
{
url: "https://x402.org/facilitator", // for testnet
}
));
// Implement your route
app.get("/protected-route", (c) => {
return c.json({ message: "This content is behind a paywall" });
});
serve({
fetch: app.fetch,
port: 3000
});
Full example in the repo here.
import express from "express";
import { paymentMiddleware, Network } from "x402-express";
// import { facilitator } from "@coinbase/x402"; // For mainnet
const app = express();
app.use(paymentMiddleware(
"0xYourAddress", // your receiving wallet address
{ // Route configurations for protected endpoints
"GET /weather": {
// USDC amount in dollars
price: "$0.001",
network: "base-sepolia", // for mainnet, see Running on Mainnet section
},
},
{
url: "https://x402.org/facilitator", // for testnet
}
));
// Implement your route
app.get("/weather", (req, res) => {
res.send({
report: {
weather: "sunny",
temperature: 70,
},
});
});
app.listen(4021, () => {
console.log(`Server listening at http://localhost:4021`);
});
Full example in the repo here.
import express from "express";
import { paymentMiddleware, Network } from "x402-express";
// import { facilitator } from "@coinbase/x402"; // For mainnet
const app = express();
app.use(paymentMiddleware(
"0xYourAddress", // your receiving wallet address
{ // Route configurations for protected endpoints
"GET /weather": {
// USDC amount in dollars
price: "$0.001",
network: "base-sepolia", // for mainnet, see Running on Mainnet section
},
},
{
url: "https://x402.org/facilitator", // for testnet
}
));
// Implement your route
app.get("/weather", (req, res) => {
res.send({
report: {
weather: "sunny",
temperature: 70,
},
});
});
app.listen(4021, () => {
console.log(`Server listening at http://localhost:4021`);
});
Full example in the repo here.
import { paymentMiddleware, Network } from 'x402-next';
// import { facilitator } from "@coinbase/x402"; // For mainnet
// Configure the payment middleware
export const middleware = paymentMiddleware(
"0xYourAddress", // your receiving wallet address
{ // Route configurations for protected endpoints
'/protected': {
price: '$0.01',
network: "base-sepolia", // for mainnet, see Running on Mainnet section
config: {
description: 'Access to protected content'
}
},
},
{
url: "https://x402.org/facilitator", // for testnet
}
);
// Configure which paths the middleware should run on
export const config = {
matcher: [
'/protected/:path*',
]
};
Full example in the repo here.
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { paymentMiddleware, Network } from "x402-hono";
// import { facilitator } from "@coinbase/x402"; // For mainnet
const app = new Hono();
// Configure the payment middleware
app.use(paymentMiddleware(
"0xYourAddress", // your receiving wallet address
{ // Route configurations for protected endpoints
"/protected-route": {
price: "$0.10",
network: "base-sepolia", // for mainnet, see Running on Mainnet section
config: {
description: "Access to premium content",
}
}
},
{
url: "https://x402.org/facilitator", // for testnet
}
));
// Implement your route
app.get("/protected-route", (c) => {
return c.json({ message: "This content is behind a paywall" });
});
serve({
fetch: app.fetch,
port: 3000
});
Full example in the repo here.
import os
from typing import Any, Dict
from dotenv import load_dotenv
from fastapi import FastAPI
from x402.fastapi.middleware import require_payment
from x402.types import EIP712Domain, TokenAmount, TokenAsset
# Load environment variables
load_dotenv()
app = FastAPI()
# Apply payment middleware to specific routes
app.middleware("http")(
require_payment(
path="/weather",
price="$0.001",
pay_to_address="0xYourAddress",
network="base-sepolia", # for mainnet, see Running on Mainnet section
)
)
@app.get("/weather")
async def get_weather() -> Dict[str, Any]:
return {
"report": {
"weather": "sunny",
"temperature": 70,
}
}
Full example in the repo here.
import os
from typing import Any, Dict
from dotenv import load_dotenv
from fastapi import FastAPI
from x402.fastapi.middleware import require_payment
from x402.types import EIP712Domain, TokenAmount, TokenAsset
# Load environment variables
load_dotenv()
app = FastAPI()
# Apply payment middleware to specific routes
app.middleware("http")(
require_payment(
path="/weather",
price="$0.001",
pay_to_address="0xYourAddress",
network="base-sepolia", # for mainnet, see Running on Mainnet section
)
)
@app.get("/weather")
async def get_weather() -> Dict[str, Any]:
return {
"report": {
"weather": "sunny",
"temperature": 70,
}
}
Full example in the repo here.
import os
from flask import Flask, jsonify
from dotenv import load_dotenv
from x402.flask.middleware import PaymentMiddleware
from x402.types import EIP712Domain, TokenAmount, TokenAsset
# Load environment variables
load_dotenv()
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="0xYourAddress",
network="base-sepolia", # for mainnet, see Running on Mainnet section
)
@app.route("/weather")
def get_weather():
return jsonify({
"report": {
"weather": "sunny",
"temperature": 70,
}
})
Payment Middleware Configuration Interface:
interface PaymentMiddlewareConfig {
description?: string; // Description of the payment
mimeType?: string; // MIME type of the resource
maxTimeoutSeconds?: number; // Maximum time for payment (default: 60)
outputSchema?: Record; // JSON schema for the response
customPaywallHtml?: string; // Custom HTML for the paywall
resource?: string; // Resource URL (defaults to request URL)
}
When a request is made to these routes without payment, your server will respond with the HTTP 402 Payment Required code and payment instructions.
3. Test Your Integration
To verify:
- Make a request to your endpoint (e.g.,
curl http://localhost:3000/your-endpoint
).
- The server responds with a 402 Payment Required, including payment instructions in the body.
- Complete the payment using a compatible client, wallet, or automated agent. This typically involves signing a payment payload, which is handled by the client SDK detailed in the Quickstart for Buyers.
- Retry the request, this time including the
X-PAYMENT
header containing the cryptographic proof of payment (payment payload).
- The server verifies the payment via the facilitator and, if valid, returns your actual API response (e.g.,
{ "data": "Your paid API response." }
).
4. Error Handling
- If you run into trouble, check out the examples in the repo for more context and full code.
npm install
the dependencies in each example
Running on Mainnet
Once you’ve tested your integration on testnet, you’re ready to accept real payments on mainnet. Here’s what you need to do:
1. Set up CDP API Keys
To use the mainnet facilitator, you’ll need a Coinbase Developer Platform account:
- Sign up at cdp.coinbase.com
- Create a new project
- Generate API credentials
- Set the following environment variables:
CDP_API_KEY_ID=your-api-key-id
CDP_API_KEY_SECRET=your-api-key-secret
2. Update Your Code
Replace the testnet configuration with mainnet settings:
// Change your imports
import { facilitator } from "@coinbase/x402";
// Update the middleware configuration
app.use(paymentMiddleware(
"0xYourAddress",
{
"GET /weather": {
price: "$0.001",
network: "base",
},
},
facilitator // this was previously { url: "https://x402.org/facilitator" }
));
// or for Next.js
export const middleware = paymentMiddleware(
"0xYourAddress",
{
"GET /weather": {
price: "$0.001",
network: "base",
},
},
facilitator // this was previously { url: "https://x402.org/facilitator" }
));
// Change your imports
import { facilitator } from "@coinbase/x402";
// Update the middleware configuration
app.use(paymentMiddleware(
"0xYourAddress",
{
"GET /weather": {
price: "$0.001",
network: "base",
},
},
facilitator // this was previously { url: "https://x402.org/facilitator" }
));
// or for Next.js
export const middleware = paymentMiddleware(
"0xYourAddress",
{
"GET /weather": {
price: "$0.001",
network: "base",
},
},
facilitator // this was previously { url: "https://x402.org/facilitator" }
));
from cdp.x402 import create_facilitator_config
facilitator_config = create_facilitator_config(
api_key_id=CDP_API_KEY_ID,
api_key_secret=CDP_API_KEY_SECRET,
)
# Update the middleware configuration (FastAPI)
app.middleware("http")(
require_payment(
path="/weather",
price="$0.001",
pay_to_address="0xYourAddress",
network="base", # Changed from "base-sepolia"
facilitator_config=facilitator_config,
)
)
# Or for Flask:
payment_middleware.add(
path="/weather",
price="$0.001",
pay_to_address="0xYourAddress",
network="base", # Changed from "base-sepolia"
facilitator_config=facilitator_config,
)
3. Update Your Wallet
Make sure your receiving wallet address (0xYourAddress
) is a real mainnet address where you want to receive USDC payments.
4. Test with Real Payments
Before going live:
- Test with small amounts first
- Verify payments are arriving in your wallet
- Monitor the facilitator for any issues
Mainnet transactions involve real money. Always test thoroughly on testnet first and start with small amounts on mainnet.
Next Steps
For questions or support, join our Discord.
Summary
This quickstart covered:
- Installing the x402 SDK and relevant middleware
- Adding payment middleware to your API and configuring it
- Testing your integration
Your API is now ready to accept crypto payments through x402.