Hands-On Tutorial15 Min Build
18 min read

x402 SDK Tutorial:
Build a Paid API Endpoint
in 15 Minutes

From npm init to a deployed API that accepts USDC payments from AI agents. Complete code. No filler.

Hono

12KB

x402-core

Types + Validation

x402-hono

Middleware

x402-solana

Verification

USDC

Solana + Base

What You Will Build

A paid web scraping API that accepts USDC payments from AI agents via the x402 protocol. An agent sends a request, gets a 402 response with payment terms, pays on Solana or Base, and receives scraped data through a Proxies.sx mobile proxy. Zero accounts. Zero API keys. Pure HTTP + crypto.

Hono API Server

TypeScript-first, 12KB, runs on Node/Deno/Bun/Workers

x402 Payment Gate

x402Middleware verifies USDC before granting access

Mobile Proxy Scraping

Fetches through real 4G/5G IPs via Proxies.sx

Edge Deployed

Cloudflare Workers or any Node runtime

Before You Start

Prerequisites

Node.js 18+

node --version to check

npm or pnpm

Package manager

A Solana or Base wallet

To receive USDC payments

Proxies.sx account (optional)

For the scraping endpoint demo

curl or httpie

To test locally

Cloudflare account (optional)

For edge deployment

Step 1 of 7

Initialize the Project & Install Dependencies

Three commands. Under 30 seconds.

bash
mkdir my-paid-api && cd my-paid-api && npm init -y
bash
npm i hono @proxies-sx/x402-core @proxies-sx/x402-hono @proxies-sx/x402-solana

What Each Package Does

hono

Ultrafast web framework. 12KB gzipped. TypeScript-first. Runs on Node.js, Deno, Bun, Cloudflare Workers, AWS Lambda, and Vercel Edge Functions. Think of it as Express but 40x smaller with native TypeScript support.

@proxies-sx/x402-core

Core types and utilities. Exports the PaymentRequirement type (defines what a 402 response contains), createPaymentRequirement() (builds a standards-compliant payment requirement object), and validatePaymentSignature() (verifies that an X-PAYMENT header contains a valid cryptographic signature).

@proxies-sx/x402-hono

Hono middleware. Exports x402Middleware() which intercepts requests, checks for payment proof, returns 402 with PaymentRequirement if missing, and calls your verify function to validate on-chain payments before allowing access.

@proxies-sx/x402-solana

Solana payment verification. Exports verifySolanaPayment() which takes a transaction signature, connects to the Solana RPC, fetches the parsed transaction, checks for a USDC SPL token transfer (mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v), verifies the amount meets the price, and confirms the recipient matches your wallet.

If you also want Base (Ethereum L2) payment verification, install @proxies-sx/x402-base. This tutorial focuses on Solana, but we show multi-network configuration in Step 3.

Step 2 of 7

Configure TypeScript

Strict mode, ESM output, Node 18 target.

tsconfig.json
json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "types": ["node"]
  },
  "include": ["src/**/*"]
}

Also update your package.json to add "type": "module" and a dev script:

package.json
json
{
  "name": "my-paid-api",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "npx tsx watch src/index.ts",
    "build": "tsc",
    "start": "node dist/index.js"
  },
  "dependencies": {
    "hono": "^4.7.0",
    "@proxies-sx/x402-core": "^1.0.0",
    "@proxies-sx/x402-hono": "^1.0.0",
    "@proxies-sx/x402-solana": "^1.0.0"
  },
  "devDependencies": {
    "tsx": "^4.0.0",
    "typescript": "^5.5.0",
    "@types/node": "^22.0.0"
  }
}
bash
npm i -D tsx typescript @types/node
Step 3 of 7

Create the Hono App with x402 Middleware

This is the core of your paid API. Every line is annotated.

src/index.ts
typescript
// src/index.ts
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { serve } from '@hono/node-server'

import {
  type PaymentRequirement,
  createPaymentRequirement,
  validatePaymentSignature,
} from '@proxies-sx/x402-core'

import { x402Middleware } from '@proxies-sx/x402-hono'
import { verifySolanaPayment } from '@proxies-sx/x402-solana'

// ─── Configuration ───────────────────────────────────────────────
// Your Solana wallet that receives USDC payments
const SOLANA_RECIPIENT = '6eUdVwsPArTxwVqEARYGCh4S2qwW2zCs7jSEDRpxydnv'

// Your Base (Ethereum L2) wallet (optional, for multi-network)
const BASE_RECIPIENT = '0xF8cD900794245fc36CBE65be9afc23CDF5103042'

// USDC token addresses
const SOLANA_USDC = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'
const BASE_USDC = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'

// Proxies.sx credentials (from client.proxies.sx dashboard)
const PROXY_HOST = process.env.PROXY_HOST || 'us.proxies.sx'
const PROXY_PORT = parseInt(process.env.PROXY_PORT || '5057')
const PROXY_USER = process.env.PROXY_USER || 'your_username'
const PROXY_PASS = process.env.PROXY_PASS || 'your_password'

// ─── Hono App ────────────────────────────────────────────────────
const app = new Hono()

// Global middleware
app.use('*', cors())
app.use('*', logger())

// ─── Health Check (free, no payment required) ────────────────────
app.get('/', (c) => {
  return c.json({
    service: 'my-paid-api',
    version: '1.0.0',
    protocol: 'x402',
    endpoints: {
      '/api/scrape': {
        method: 'POST',
        price: '$0.05 per request',
        payment: 'USDC on Solana or Base',
        description: 'Scrape any URL through a real 4G/5G mobile proxy',
      },
      '/api/bulk-scrape': {
        method: 'POST',
        price: '$0.20 per request (up to 10 URLs)',
        payment: 'USDC on Solana or Base',
        description: 'Batch scrape multiple URLs',
      },
    },
    docs: 'https://agents.proxies.sx/marketplace/',
  })
})

// ─── x402 Middleware for /api/* routes ────────────────────────────
//
// x402Middleware() configuration:
//
//   price: number
//     Amount in micro-units. 1 micro-unit = $0.000001.
//     50000 = $0.05, 200000 = $0.20, 5000000 = $5.00
//
//   recipient: string
//     Your wallet address that receives payment.
//
//   networks: string[]
//     Which blockchains to accept. Options: 'solana', 'base'.
//     The agent picks which one to use based on its funding.
//
//   description: string
//     Human-readable description shown in the 402 response body.
//
//   verify: (paymentHeader: string, requirement: PaymentRequirement) => Promise<boolean>
//     Your custom verification function. Called when the client
//     retries with an X-PAYMENT header. Must return true if the
//     on-chain payment is valid.
//
//   resource: string (optional)
//     Identifies the resource being purchased. Included in the
//     PaymentRequirement for the agent's reference.

// ─── Single Scrape Endpoint ($0.05/request) ──────────────────────
app.use(
  '/api/scrape',
  x402Middleware({
    price: 50000, // $0.05 in micro-units (50000 * $0.000001)
    recipient: SOLANA_RECIPIENT,
    networks: ['solana', 'base'],
    description: 'Scrape a URL through a real 4G/5G mobile proxy',
    resource: '/api/scrape',
    verify: async (paymentHeader: string, requirement: PaymentRequirement) => {
      // Step 1: Validate the cryptographic signature on the header
      const signatureValid = validatePaymentSignature(paymentHeader)
      if (!signatureValid) return false

      // Step 2: Verify the on-chain payment
      // Parse the payment header to get the transaction signature
      const payment = JSON.parse(
        Buffer.from(paymentHeader, 'base64').toString()
      )

      if (payment.network === 'solana') {
        const result = await verifySolanaPayment({
          txSignature: payment.txSignature,
          expectedRecipient: SOLANA_RECIPIENT,
          expectedAmount: requirement.price,
          usdcMint: SOLANA_USDC,
        })
        return result.verified
      }

      // For Base verification, use @proxies-sx/x402-base
      // (similar pattern, checks ERC20 Transfer event)
      return false
    },
  })
)

// ─── Bulk Scrape Endpoint ($0.20/request, up to 10 URLs) ─────────
app.use(
  '/api/bulk-scrape',
  x402Middleware({
    price: 200000, // $0.20 in micro-units
    recipient: SOLANA_RECIPIENT,
    networks: ['solana'],
    description: 'Batch scrape up to 10 URLs through mobile proxies',
    resource: '/api/bulk-scrape',
    verify: async (paymentHeader: string, requirement: PaymentRequirement) => {
      const signatureValid = validatePaymentSignature(paymentHeader)
      if (!signatureValid) return false

      const payment = JSON.parse(
        Buffer.from(paymentHeader, 'base64').toString()
      )

      const result = await verifySolanaPayment({
        txSignature: payment.txSignature,
        expectedRecipient: SOLANA_RECIPIENT,
        expectedAmount: requirement.price,
        usdcMint: SOLANA_USDC,
      })
      return result.verified
    },
  })
)

// ─── Route Handlers ──────────────────────────────────────────────

// POST /api/scrape — single URL scrape
app.post('/api/scrape', async (c) => {
  const body = await c.req.json<{ url: string; headers?: Record<string, string> }>()

  if (!body.url) {
    return c.json({ error: 'missing_url', message: 'Provide a "url" field' }, 400)
  }

  try {
    // Build proxy URL for fetch (SOCKS5 via Proxies.sx)
    const proxyUrl = `http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}`

    // Fetch through mobile proxy
    const response = await fetch(body.url, {
      headers: {
        'User-Agent':
          'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15',
        ...body.headers,
      },
      // @ts-ignore — proxy support varies by runtime
      proxy: proxyUrl,
    })

    const html = await response.text()

    return c.json({
      success: true,
      url: body.url,
      status: response.status,
      content_length: html.length,
      content: html,
      proxy: {
        provider: 'proxies.sx',
        type: '4G/5G mobile',
        country: 'US',
      },
      payment: {
        protocol: 'x402',
        amount: '$0.05',
        currency: 'USDC',
      },
    })
  } catch (err) {
    return c.json(
      {
        error: 'scrape_failed',
        message: err instanceof Error ? err.message : 'Unknown error',
      },
      500
    )
  }
})

// POST /api/bulk-scrape — up to 10 URLs
app.post('/api/bulk-scrape', async (c) => {
  const body = await c.req.json<{ urls: string[] }>()

  if (!body.urls || !Array.isArray(body.urls)) {
    return c.json({ error: 'missing_urls', message: 'Provide a "urls" array' }, 400)
  }

  if (body.urls.length > 10) {
    return c.json({ error: 'too_many_urls', message: 'Maximum 10 URLs per request' }, 400)
  }

  const proxyUrl = `http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}`

  const results = await Promise.allSettled(
    body.urls.map(async (url) => {
      const response = await fetch(url, {
        headers: {
          'User-Agent':
            'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15',
        },
        // @ts-ignore
        proxy: proxyUrl,
      })
      const html = await response.text()
      return { url, status: response.status, content_length: html.length, content: html }
    })
  )

  return c.json({
    success: true,
    total: body.urls.length,
    results: results.map((r, i) =>
      r.status === 'fulfilled'
        ? r.value
        : { url: body.urls[i], error: (r.reason as Error).message }
    ),
    payment: { protocol: 'x402', amount: '$0.20', currency: 'USDC' },
  })
})

// ─── Start Server ────────────────────────────────────────────────
const port = parseInt(process.env.PORT || '3000')

serve({ fetch: app.fetch, port }, (info) => {
  console.log(`
  ┌─────────────────────────────────────────────┐
  │  x402 Paid API running on port ${info.port}        │
  │                                             │
  │  Free:  GET  /                              │
  │  Paid:  POST /api/scrape       ($0.05)      │
  │  Paid:  POST /api/bulk-scrape  ($0.20)      │
  │                                             │
  │  Payment: USDC on Solana or Base            │
  │  Recipient: ${SOLANA_RECIPIENT.slice(0, 8)}...   │
  └─────────────────────────────────────────────┘
  `)
})

Key Concepts in This File

1

Micro-unit pricing

price: 50000 means $0.05. The formula is price * $0.000001. This avoids floating-point issues. For $5.00, set price: 5000000.

2

Multi-network support

networks: ['solana', 'base'] tells the agent it can pay on either chain. The 402 response includes payment requirements for each network with the appropriate recipient address and token contract.

3

Verify function

Called when the client retries with X-PAYMENT header. First validates the signature with validatePaymentSignature(), then checks the actual on-chain transfer with verifySolanaPayment(). Both must pass for access to be granted.

4

PaymentRequirement type

Exported from @proxies-sx/x402-core. Contains price, recipient, network, token, description, and resource. The middleware automatically serializes this into the 402 response body.

Step 4 of 7

Deep Dive: x402-core Types & Functions

Understanding the type system that powers every x402 transaction.

@proxies-sx/x402-core — Type Reference
typescript
// Types exported by @proxies-sx/x402-core

/**
 * PaymentRequirement — the data structure returned in a 402 response.
 * This tells the paying agent exactly what to do.
 */
export interface PaymentRequirement {
  /** Price in micro-units. 1 = $0.000001. Integer only. */
  price: number

  /** Wallet address that should receive the payment */
  recipient: string

  /** Blockchain network: 'solana' | 'base' */
  network: string

  /** Token contract address (USDC) */
  token: string

  /** Human-readable description of what the payment is for */
  description: string

  /** The API resource/endpoint being purchased */
  resource?: string

  /** ISO 8601 expiry timestamp for this payment requirement */
  expiry?: string

  /** Unique nonce to prevent replay attacks */
  nonce?: string
}

/**
 * createPaymentRequirement() — builds a PaymentRequirement with
 * sensible defaults (generates nonce, sets 5-minute expiry).
 *
 * You rarely call this directly because x402Middleware does it
 * for you, but it is useful for custom 402 responses.
 */
export function createPaymentRequirement(opts: {
  price: number
  recipient: string
  network: string
  token?: string      // defaults to USDC address for the network
  description?: string
  resource?: string
  expiryMinutes?: number  // default: 5
}): PaymentRequirement

/**
 * validatePaymentSignature() — checks the X-PAYMENT header.
 * The header is a base64-encoded JSON object containing:
 *   - txSignature: the on-chain transaction hash
 *   - network: which chain the tx is on
 *   - timestamp: when the payment was made
 *   - signature: cryptographic signature over the above fields
 *
 * Returns true if the signature is mathematically valid.
 * Does NOT check the blockchain — that is what
 * verifySolanaPayment() or verifyBasePayment() does.
 */
export function validatePaymentSignature(
  paymentHeader: string
): boolean
@proxies-sx/x402-solana — API Reference
typescript
// @proxies-sx/x402-solana — Solana verification

import { Connection, ParsedTransactionWithMeta } from '@solana/web3.js'

export interface SolanaPaymentResult {
  /** Whether the payment was verified successfully */
  verified: boolean

  /** The parsed Solana transaction (if found) */
  transaction?: ParsedTransactionWithMeta

  /** Transfer amount in USDC (human-readable, e.g. "0.05") */
  amount?: string

  /** Sender wallet address */
  sender?: string

  /** Error message if verification failed */
  error?: string
}

/**
 * verifySolanaPayment() — the core verification function.
 *
 * 1. Connects to Solana RPC (mainnet-beta by default)
 * 2. Fetches the parsed transaction by signature
 * 3. Looks for a USDC SPL token transfer instruction
 * 4. Checks the transfer destination matches expectedRecipient
 * 5. Checks the transfer amount >= expectedAmount (in micro-units)
 * 6. Returns SolanaPaymentResult with verified: true or false
 */
export async function verifySolanaPayment(opts: {
  /** Transaction signature (hash) from the X-PAYMENT header */
  txSignature: string

  /** Your Solana wallet that should have received USDC */
  expectedRecipient: string

  /** Required amount in micro-units (same as PaymentRequirement.price) */
  expectedAmount: number

  /** USDC SPL token mint address */
  usdcMint?: string  // default: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

  /** Custom Solana RPC URL */
  rpcUrl?: string    // default: https://api.mainnet-beta.solana.com

  /** Max retries for RPC calls */
  maxRetries?: number  // default: 3

  /** Require transaction to be finalized (vs confirmed) */
  requireFinalized?: boolean  // default: false
}): Promise<SolanaPaymentResult>

Network Token Addresses

Solana

USDC (SPL Token)

EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

Proxies.sx Recipient

6eUdVwsPArTxwVqEARYGCh4S2qwW2zCs7jSEDRpxydnv

Base (Ethereum L2)

USDC (ERC20)

0x833589fcd6edb6e08f4c7c32d4f71b54bda02913

Proxies.sx Recipient

0xF8cD900794245fc36CBE65be9afc23CDF5103042
Step 5 of 7

Test Locally with curl

Start the server, hit the endpoint, and see the 402 flow in action.

First, start the development server:

bash
npm run dev

In another terminal, hit the health endpoint (free, no payment):

bash
curl http://localhost:3000/

# Response:
# {
#   "service": "my-paid-api",
#   "version": "1.0.0",
#   "protocol": "x402",
#   "endpoints": {
#     "/api/scrape": {
#       "method": "POST",
#       "price": "$0.05 per request",
#       "payment": "USDC on Solana or Base"
#     }
#   }
# }

Now hit the paid endpoint without payment:

bash
curl -s -w "\nHTTP Status: %{http_code}\n" \
  -X POST http://localhost:3000/api/scrape \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'

# HTTP Status: 402
#
# Response body:
# {
#   "status": 402,
#   "message": "Payment Required",
#   "paymentRequirements": [
#     {
#       "price": 50000,
#       "recipient": "6eUdVwsPArTxwVqEARYGCh4S2qwW2zCs7jSEDRpxydnv",
#       "network": "solana",
#       "token": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
#       "description": "Scrape a URL through a real 4G/5G mobile proxy",
#       "resource": "/api/scrape",
#       "expiry": "2026-02-08T12:05:00.000Z",
#       "nonce": "a1b2c3d4e5f6"
#     },
#     {
#       "price": 50000,
#       "recipient": "0xF8cD900794245fc36CBE65be9afc23CDF5103042",
#       "network": "base",
#       "token": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
#       "description": "Scrape a URL through a real 4G/5G mobile proxy",
#       "resource": "/api/scrape",
#       "expiry": "2026-02-08T12:05:00.000Z",
#       "nonce": "a1b2c3d4e5f6"
#     }
#   ]
# }

402 Response Anatomy

The 402 response is machine-readable. An AI agent parses this, picks a network, submits a USDC transfer, and retries with the X-PAYMENT header. Here is what each field means:

  • price: 50000$0.05 in micro-units (50000 * $0.000001)
  • recipientYour wallet. The agent sends USDC here.
  • network"solana" or "base" — which chain to pay on
  • tokenUSDC contract address on that chain
  • expiryPayment must arrive before this timestamp (5 min default)
  • nonceUnique per-request; prevents replay of old payments

Once an agent pays on-chain and gets a transaction signature, it retries with the X-PAYMENT header:

bash
# After the agent pays USDC on Solana and gets tx signature:
PAYMENT_HEADER=$(echo -n '{
  "txSignature": "5KtP...real_solana_tx_signature...vN3",
  "network": "solana",
  "timestamp": "2026-02-08T12:01:30.000Z",
  "signature": "base64_ed25519_signature_here"
}' | base64)

curl -s -X POST http://localhost:3000/api/scrape \
  -H "Content-Type: application/json" \
  -H "X-PAYMENT: $PAYMENT_HEADER" \
  -d '{"url": "https://example.com"}'

# HTTP Status: 200
#
# Response:
# {
#   "success": true,
#   "url": "https://example.com",
#   "status": 200,
#   "content_length": 1256,
#   "content": "<!doctype html>...",
#   "proxy": {
#     "provider": "proxies.sx",
#     "type": "4G/5G mobile",
#     "country": "US"
#   },
#   "payment": {
#     "protocol": "x402",
#     "amount": "$0.05",
#     "currency": "USDC"
#   }
# }

The flow is: request -> 402 -> agent pays on-chain -> retry with proof -> 200 with data. This entire round-trip takes under 3 seconds on Solana (most of that is transaction confirmation).

Step 6 of 7

Deploy to Cloudflare Workers

Hono runs natively on Workers. Zero config changes to your app code.

Hono was designed for edge runtimes. Your src/index.ts works on Cloudflare Workers with a single change to the entry point. First, install Wrangler and create the config:

bash
npm i -D wrangler
wrangler.toml
toml
# wrangler.toml
name = "my-paid-api"
main = "src/worker.ts"
compatibility_date = "2026-02-01"

[vars]
PROXY_HOST = "us.proxies.sx"
PROXY_PORT = "5057"

# Secrets (set via wrangler secret put):
# PROXY_USER, PROXY_PASS

Create the Workers entry point. The only difference from the Node version is the export format:

src/worker.ts
typescript
// src/worker.ts
// Cloudflare Workers entry point for Hono
//
// Your app code in src/index.ts stays identical.
// This file just re-exports the Hono app in the
// format Workers expects.

import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import {
  type PaymentRequirement,
  validatePaymentSignature,
} from '@proxies-sx/x402-core'
import { x402Middleware } from '@proxies-sx/x402-hono'
import { verifySolanaPayment } from '@proxies-sx/x402-solana'

type Bindings = {
  PROXY_HOST: string
  PROXY_PORT: string
  PROXY_USER: string
  PROXY_PASS: string
}

const app = new Hono<{ Bindings: Bindings }>()

app.use('*', cors())
app.use('*', logger())

// Health check
app.get('/', (c) => {
  return c.json({
    service: 'my-paid-api',
    version: '1.0.0',
    protocol: 'x402',
    runtime: 'cloudflare-workers',
  })
})

const SOLANA_RECIPIENT = '6eUdVwsPArTxwVqEARYGCh4S2qwW2zCs7jSEDRpxydnv'
const SOLANA_USDC = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'

// x402 middleware for paid endpoints
app.use(
  '/api/scrape',
  x402Middleware({
    price: 50000,
    recipient: SOLANA_RECIPIENT,
    networks: ['solana'],
    description: 'Scrape a URL through a real 4G/5G mobile proxy',
    resource: '/api/scrape',
    verify: async (paymentHeader: string, requirement: PaymentRequirement) => {
      const signatureValid = validatePaymentSignature(paymentHeader)
      if (!signatureValid) return false

      const payment = JSON.parse(
        Buffer.from(paymentHeader, 'base64').toString()
      )

      const result = await verifySolanaPayment({
        txSignature: payment.txSignature,
        expectedRecipient: SOLANA_RECIPIENT,
        expectedAmount: requirement.price,
        usdcMint: SOLANA_USDC,
      })
      return result.verified
    },
  })
)

app.post('/api/scrape', async (c) => {
  const body = await c.req.json<{ url: string }>()

  if (!body.url) {
    return c.json({ error: 'missing_url' }, 400)
  }

  const { PROXY_HOST, PROXY_PORT, PROXY_USER, PROXY_PASS } = c.env

  // On Workers, use fetch with cf.proxy or a tunnel
  const response = await fetch(body.url, {
    headers: {
      'User-Agent':
        'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15',
    },
  })

  const html = await response.text()

  return c.json({
    success: true,
    url: body.url,
    status: response.status,
    content_length: html.length,
    content: html,
    proxy: { provider: 'proxies.sx', type: '4G/5G mobile' },
    payment: { protocol: 'x402', amount: '$0.05', currency: 'USDC' },
  })
})

export default app

Set your secrets and deploy:

bash
# Set secrets (not stored in code)
wrangler secret put PROXY_USER
wrangler secret put PROXY_PASS

# Deploy
wrangler deploy

# Output:
# Uploaded my-paid-api (1.42 sec)
# Published my-paid-api (0.31 sec)
#   https://my-paid-api.your-subdomain.workers.dev

# Test it:
curl https://my-paid-api.your-subdomain.workers.dev/

Alternative Deployment Targets

Hono runs anywhere. The same src/index.ts works on:

Node.js

npm start

Uses @hono/node-server

Deno

deno run src/index.ts

Native Deno support

Bun

bun src/index.ts

Fastest startup

AWS Lambda

sst deploy

Via @hono/aws-lambda

Vercel Edge

vercel

Edge Functions

Docker

docker run

Any container platform

Step 7 of 7

Submit to the Marketplace

List your API on agents.proxies.sx/marketplace/ so AI agents can discover and pay for it.

The Proxies.sx marketplace is a directory of x402-enabled services. When you list your API, agents can discover it programmatically, read the pricing, and start paying. Create a service manifest following the spec at agents.proxies.sx/marketplace/submit.md:

service-manifest.yaml
yaml
# x402 Service Manifest
# Submit this to agents.proxies.sx/marketplace/

service:
  name: "Mobile Proxy Scraper API"
  version: "1.0.0"
  description: >
    Scrape any URL through real 4G/5G mobile proxies.
    Returns full HTML content with US mobile IP.
    Powered by Proxies.sx infrastructure.

  url: "https://my-paid-api.your-subdomain.workers.dev"

  protocol: "x402"
  payment:
    currency: "USDC"
    networks:
      - name: "solana"
        recipient: "6eUdVwsPArTxwVqEARYGCh4S2qwW2zCs7jSEDRpxydnv"
        token: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
      - name: "base"
        recipient: "0xF8cD900794245fc36CBE65be9afc23CDF5103042"
        token: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"

  endpoints:
    - path: "/api/scrape"
      method: "POST"
      price: 50000        # $0.05 in micro-units
      description: "Scrape a single URL through mobile proxy"
      input:
        content_type: "application/json"
        schema:
          url: "string (required)"
          headers: "object (optional)"
      output:
        content_type: "application/json"

    - path: "/api/bulk-scrape"
      method: "POST"
      price: 200000       # $0.20 in micro-units
      description: "Batch scrape up to 10 URLs"
      input:
        content_type: "application/json"
        schema:
          urls: "string[] (required, max 10)"
      output:
        content_type: "application/json"

  category: "web-scraping"
  tags:
    - scraping
    - mobile-proxy
    - 4g-5g
    - data-extraction

  contact:
    email: "you@example.com"
    website: "https://your-website.com"

  sla:
    uptime: "99.5%"
    avg_response_time: "2s"
    rate_limit: "100 requests/minute"

Submit the Manifest

Push your YAML to agents.proxies.sx/marketplace/. The team reviews and approves within 24 hours.

Agents Discover You

AI agents query the marketplace directory, find your API by category or tag, and read the pricing.

Get Paid in USDC

Payments go directly to your wallet. No middleman fees. No invoices. Just on-chain USDC.

Complete Flow

End-to-End Payment Flow

What happens when an AI agent calls your API.

1

Agent sends POST /api/scrape

Standard HTTP request with JSON body. No authentication headers needed.

2

x402Middleware checks for X-PAYMENT header

Header missing? Return 402 with PaymentRequirement array (one per network).

3

Agent parses 402 response

Reads price (50000 micro-units = $0.05), recipient wallet, token address, network options.

4

Agent submits USDC transfer on Solana

SPL token transfer to 6eUdVws... for 0.05 USDC. Gets transaction signature in ~400ms.

5

Agent retries with X-PAYMENT header

Header contains base64-encoded JSON: txSignature, network, timestamp, signature.

6

Middleware calls your verify function

validatePaymentSignature() checks header integrity. verifySolanaPayment() checks the blockchain.

7

Route handler executes, returns 200

Your scrape logic runs, fetches through Proxies.sx mobile proxy, returns data to the agent.

Advanced

Custom Payment Requirements

When the middleware defaults are not enough, use createPaymentRequirement() directly.

Scenarios where you need manual control: dynamic pricing based on input size, metered endpoints where price depends on parameters, or multi-tier access where the same endpoint has different price levels.

Dynamic Pricing Example
typescript
import {
  createPaymentRequirement,
  type PaymentRequirement,
} from '@proxies-sx/x402-core'

// Dynamic pricing: charge per URL in bulk requests
app.post('/api/dynamic-scrape', async (c) => {
  const body = await c.req.json<{ urls: string[] }>()
  const urlCount = body.urls?.length || 1

  // Check for payment header first
  const paymentHeader = c.req.header('X-PAYMENT')

  if (!paymentHeader) {
    // Calculate dynamic price: $0.03 per URL
    const pricePerUrl = 30000  // $0.03 in micro-units
    const totalPrice = pricePerUrl * urlCount

    // Build custom PaymentRequirement
    const requirement = createPaymentRequirement({
      price: totalPrice,
      recipient: '6eUdVwsPArTxwVqEARYGCh4S2qwW2zCs7jSEDRpxydnv',
      network: 'solana',
      description: `Scrape ${urlCount} URLs at $0.03 each = $${(totalPrice / 1000000).toFixed(2)}`,
      resource: '/api/dynamic-scrape',
      expiryMinutes: 10,  // 10 min expiry for bulk jobs
    })

    return c.json(
      {
        status: 402,
        message: 'Payment Required',
        paymentRequirements: [requirement],
        pricing: {
          per_url: '$0.03',
          url_count: urlCount,
          total: `$${(totalPrice / 1000000).toFixed(2)}`,
        },
      },
      402
    )
  }

  // Verify payment and proceed...
  // (same verify logic as before)
})
Tiered Pricing Example
typescript
// Example: Tiered access with different prices
import { createPaymentRequirement } from '@proxies-sx/x402-core'

// Premium tier: residential proxy + JS rendering
// Standard tier: mobile proxy, HTML only
app.post('/api/premium-scrape', async (c) => {
  const paymentHeader = c.req.header('X-PAYMENT')

  if (!paymentHeader) {
    // Offer two payment tiers
    const standard = createPaymentRequirement({
      price: 50000,       // $0.05
      recipient: '6eUdVwsPArTxwVqEARYGCh4S2qwW2zCs7jSEDRpxydnv',
      network: 'solana',
      description: 'Standard: mobile proxy, HTML only',
      resource: '/api/premium-scrape?tier=standard',
    })

    const premium = createPaymentRequirement({
      price: 250000,      // $0.25
      recipient: '6eUdVwsPArTxwVqEARYGCh4S2qwW2zCs7jSEDRpxydnv',
      network: 'solana',
      description: 'Premium: residential proxy + JavaScript rendering',
      resource: '/api/premium-scrape?tier=premium',
    })

    return c.json({
      status: 402,
      message: 'Payment Required',
      paymentRequirements: [standard, premium],
    }, 402)
  }

  // Verify and determine which tier was paid for...
})
Production

Error Handling & Production Hardening

Make your x402 API production-ready.

Production Verification
typescript
import { verifySolanaPayment } from '@proxies-sx/x402-solana'

// Production verify function with comprehensive error handling
async function productionVerify(
  paymentHeader: string,
  requirement: PaymentRequirement
): Promise<boolean> {
  try {
    // 1. Validate header format
    let payment: {
      txSignature: string
      network: string
      timestamp: string
      signature: string
    }

    try {
      payment = JSON.parse(
        Buffer.from(paymentHeader, 'base64').toString()
      )
    } catch {
      console.error('[x402] Invalid payment header encoding')
      return false
    }

    // 2. Check required fields
    if (!payment.txSignature || !payment.network || !payment.timestamp) {
      console.error('[x402] Missing required payment fields')
      return false
    }

    // 3. Check timestamp freshness (reject payments older than 5 min)
    const paymentTime = new Date(payment.timestamp).getTime()
    const now = Date.now()
    if (now - paymentTime > 5 * 60 * 1000) {
      console.error('[x402] Payment timestamp too old:', payment.timestamp)
      return false
    }

    // 4. Validate cryptographic signature
    const signatureValid = validatePaymentSignature(paymentHeader)
    if (!signatureValid) {
      console.error('[x402] Invalid payment signature')
      return false
    }

    // 5. Verify on-chain (with retries)
    if (payment.network === 'solana') {
      const result = await verifySolanaPayment({
        txSignature: payment.txSignature,
        expectedRecipient: SOLANA_RECIPIENT,
        expectedAmount: requirement.price,
        usdcMint: SOLANA_USDC,
        maxRetries: 3,
        requireFinalized: false,  // 'confirmed' is fast enough
      })

      if (!result.verified) {
        console.error('[x402] Solana verification failed:', result.error)
        return false
      }

      console.log(`[x402] Payment verified: ${result.amount} USDC from ${result.sender}`)
      return true
    }

    console.error('[x402] Unsupported network:', payment.network)
    return false

  } catch (err) {
    console.error('[x402] Verification error:', err)
    return false
  }
}

// Replay protection: track used transaction signatures
const usedSignatures = new Set<string>()

async function verifyWithReplayProtection(
  paymentHeader: string,
  requirement: PaymentRequirement
): Promise<boolean> {
  const payment = JSON.parse(
    Buffer.from(paymentHeader, 'base64').toString()
  )

  // Reject if this tx signature was already used
  if (usedSignatures.has(payment.txSignature)) {
    console.error('[x402] Replay attempt:', payment.txSignature)
    return false
  }

  const verified = await productionVerify(paymentHeader, requirement)

  if (verified) {
    usedSignatures.add(payment.txSignature)

    // Cleanup old signatures after 1 hour
    setTimeout(() => {
      usedSignatures.delete(payment.txSignature)
    }, 60 * 60 * 1000)
  }

  return verified
}

Replay Protection

Track used tx signatures in a Set (or Redis for distributed). Reject duplicates.

Timestamp Freshness

Reject payment headers older than 5 minutes. Prevents stale payment reuse.

Signature Validation

Always call validatePaymentSignature() before on-chain verification. Fast cryptographic check.

Logging

Log every payment event: verified, failed, replayed. Essential for debugging and accounting.

Reference

Complete Project Structure

Project Tree
bash
my-paid-api/
├── src/
│   ├── index.ts          # Main Hono app (Node/Deno/Bun)
│   └── worker.ts         # Cloudflare Workers entry point
├── package.json
├── tsconfig.json
├── wrangler.toml         # Cloudflare Workers config
├── service-manifest.yaml # Marketplace submission
└── .env                  # Local env vars (not committed)

# .env contents:
PROXY_HOST=us.proxies.sx
PROXY_PORT=5057
PROXY_USER=your_username
PROXY_PASS=your_password
PORT=3000

Revenue Potential

At $0.05/request with moderate agent traffic, here is what the numbers look like:

1,000/day

$50/day

$1,500/mo

10,000/day

$500/day

$15,000/mo

100,000/day

$5,000/day

$150,000/mo

Zero platform fees. All USDC goes directly to your wallet. Compare this to Stripe (2.9% + $0.30) or RapidAPI (20% cut). See our x402 vs Stripe comparison for the full breakdown.

FAQ

Frequently Asked Questions

Your API is Ready to Earn

You just built a production-grade paid API endpoint in 15 minutes. AI agents can discover it, pay for it with USDC, and use it -- no accounts, no API keys, no human checkout flows. Now ship it.

PROXIES.SX Team

Building AI-native proxy infrastructure