Home / Documentation / x402 Protocol Specification
Standard: HTTP 402 Network: Base L2 Settlement: USDC Signing: EIP-712

x402 Protocol Specification

The standard for agentic micropayments on the open web. x402 allows AI crawlers, LLM retrieval pipelines, and autonomous research agents to access paid or protected content frictionlessly by settling micropayments over HTTP.

1. Overview & Motivation

The internet was designed with status code 402 Payment Required reserved for future digital cash protocols. For decades, the lack of ubiquitous, low-fee digital currency left HTTP 402 largely unused, forcing publishers to rely on banner ads, intrusive tracking, hard subscription paywalls, and CAPTCHAs.

With modern Layer-2 blockchain networks like Base providing sub-cent transaction costs, and autonomous AI agents consuming web content at unprecedented scale, x402 revitalizes HTTP 402 as the open standard for machine-to-machine micropayments.

Key Objective: Replace brittle scraping bans and IP blocks with an open market where autonomous agents pay fractions of a cent directly to creators per article or per request.

2. Protocol Flow

The x402 handshake completes in four discrete steps without requiring prior account registration, API keys, or pre-negotiated contracts.

Step 1

Initial Request

The AI crawler or agent issues a standard HTTP GET request with an optional Prefer: x402 header to signal client readiness for machine payment.

GET /v1/things/deep-research-report HTTP/1.1
Host: thingster.app
Accept: text/markdown, application/json
Prefer: x402
Step 2

402 Payment Challenge

The server determines the resource is monetized for agents and returns HTTP 402 with the PAYMENT-REQUIRED header (Base64-encoded challenge) and matching JSON body conforming to x402 v2.

HTTP/1.1 402 Payment Required
Content-Type: application/json
PAYMENT-REQUIRED: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQYXltZW50IHJlcXVpcmVkIiw...

{
  "x402Version": 2,
  "error": "Payment required to access content",
  "resource": {
    "url": "https://thingster.app/v1/things/deep-research-report",
    "description": "Unlock content: Deep Research Report",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "20000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97",
      "maxTimeoutSeconds": 300,
      "extra": {
        "name": "USD Coin",
        "symbol": "USDC",
        "version": "2",
        "decimals": 6
      }
    }
  ]
}
Step 3

Cryptographic Signature & Payment Retry

The agent signs an EIP-3009 transfer authorization, builds the structured x402 payload, and retries with the PAYMENT-SIGNATURE header.

GET /v1/things/deep-research-report HTTP/1.1
Host: thingster.app
Prefer: x402
Accept: application/json
PAYMENT-SIGNATURE: eyJ4NDAyVmVyc2lvbiI6MiwicGF5bG9hZCI6eyJzaWduYXR1cmUiOiIweDhhOTJmLi4uIiwiYXV0aG9yaXphdGlvbiI6eyJmcm9tIjoiMHgxMjM0Li4uIiwidG8iOiIweDQ4MzguLi4iLCJ2YWx1ZSI6IjIwMDAwIiwidmFsaWRBZnRlciI6MCwidmFsaWRCZWZvcmUiOjE3MzAwMDAwMDAsIm5vbmNlIjoiMHg5ZjRhLi4uIn19fQ==
Step 4

Verification & Content Delivery

The server validates the cryptographic authorization, records settlement, returns HTTP 200 OK with the PAYMENT-RESPONSE confirmation header, and delivers the unlocked content.

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
PAYMENT-RESPONSE: eyJ0eEhhc2giOiIweGVmMzI0MWJjLi4uIiwicGF5ZXIiOiIweDEyMzQuLi4iLCJuZXR3b3JrIjoiZWlwMTU1Ojg0NTMifQ==

{
  "thing": {
    "id": "deep-research-report",
    "unlocked": true,
    "title": "Deep Research Report",
    "body": "# Deep Research Report
Full unlocked content delivered to the agent..."
  }
}

3. HTTP Headers Specification

Server Response Headers (Status 402)

Header / Field Type Description Example
PAYMENT-REQUIRED Base64 JSON Encoded x402 v2 challenge object with accepted payment schemes and recipient addresses. eyJ4NDAyVmVyc2lv...
Response Body JSON Object Conforms to x402 v2 standard: contains x402Version: 2, resource, and accepts array. { "x402Version": 2, "accepts": [...] }
PAYMENT-RESPONSE Base64 JSON Returned on HTTP 200 with settlement confirmation, transaction hash, and payer address. eyJ0eEhhc2giOi...

Client Request Headers (Payment Retry)

Header Type Description Example
PAYMENT-SIGNATURE Base64 / JSON x402 v2 payload structure with EIP-712 / EIP-3009 signature and authorization parameters. Also accepts X-Payment. eyJ4NDAyVmVyc2lv...
Prefer String Signals x402 machine payment readiness. x402
Accept String Expected response mime types. application/json, text/markdown

4. Client Integration Examples

Integrate x402 with any HTTP client or agent framework in just a few lines of code.

JavaScript / Node.js (with viem)

import { createPublicClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { base } from 'viem/chains'

const account = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY)

async function fetchWithX402(url) {
  // 1. Initial probe request
  const initialRes = await fetch(url, {
    headers: { 'Prefer': 'x402', 'Accept': 'application/json' },
  })

  if (initialRes.status !== 402) {
    return await initialRes.json()
  }

  // 2. Parse x402 challenge from JSON body or PAYMENT-REQUIRED header
  const challenge = await initialRes.json()
  const requirement = challenge.accepts?.[0]
  if (!requirement) {
    throw new Error('No valid payment requirements found in 402 response')
  }

  const { amount, asset, payTo, extra } = requirement
  console.log(`Agent paying ${amount} atomic units (${extra?.symbol || 'USDC'}) to ${payTo}`)

  // 3. Construct and sign EIP-3009 transfer authorization
  const now = Math.floor(Date.now() / 1000)
  const validBefore = BigInt(now + 3600)
  const nonce = '0x' + Array.from(crypto.getRandomValues(new Uint8Array(32)), (b) => b.toString(16).padStart(2, '0')).join('')

  const domain = {
    name: extra?.name || 'USD Coin',
    version: extra?.version || '2',
    chainId: 8453,
    verifyingContract: asset,
  }

  const types = {
    TransferWithAuthorization: [
      { name: 'from', type: 'address' },
      { name: 'to', type: 'address' },
      { name: 'value', type: 'uint256' },
      { name: 'validAfter', type: 'uint256' },
      { name: 'validBefore', type: 'uint256' },
      { name: 'nonce', type: 'bytes32' },
    ],
  }

  const authorization = {
    from: account.address,
    to: payTo,
    value: BigInt(amount),
    validAfter: 0n,
    validBefore,
    nonce,
  }

  const signature = await account.signTypedData({
    domain,
    types,
    primaryType: 'TransferWithAuthorization',
    message: authorization,
  })

  // 4. Retry request with structured PAYMENT-SIGNATURE payload
  const paymentPayload = {
    x402Version: 2,
    payload: {
      signature,
      authorization: {
        from: account.address,
        to: payTo,
        value: amount.toString(),
        validAfter: 0,
        validBefore: Number(validBefore),
        nonce,
      },
    },
  }

  const paidRes = await fetch(url, {
    headers: {
      'PAYMENT-SIGNATURE': btoa(JSON.stringify(paymentPayload)),
      'Accept': 'application/json',
    },
  })

  return await paidRes.json()
}

Python / AI Crawler Example

import base64
import json
import os
import time
import requests

def get_thingster_content(url, agent_account):
    session = requests.Session()
    session.headers.update({"Prefer": "x402", "Accept": "application/json"})

    resp = session.get(url)
    if resp.status_code == 402:
        challenge = resp.json()
        req = challenge["accepts"][0]

        valid_before = int(time.time()) + 3600
        nonce = "0x" + os.urandom(32).hex()
        auth = {
            "from": agent_account.address,
            "to": req["payTo"],
            "value": str(req["amount"]),
            "validAfter": 0,
            "validBefore": valid_before,
            "nonce": nonce,
        }
        sig = agent_account.sign_typed_data(
            domain={
                "name": req["extra"].get("name", "USD Coin"),
                "version": req["extra"].get("version", "2"),
                "chainId": 8453,
                "verifyingContract": req["asset"],
            },
            types={"TransferWithAuthorization": [...]},
            message=auth,
        )

        payment_payload = {
            "x402Version": 2,
            "payload": {
                "signature": f"0x{sig.signature.hex()}",
                "authorization": auth,
            },
        }
        encoded = base64.b64encode(json.dumps(payment_payload).encode()).decode()

        resp = session.get(url, headers={"PAYMENT-SIGNATURE": encoded})

    return resp.json()

5. Publisher Configuration

As a publisher on Thingster, you configure how bots interact with your content through simple settings in your dashboard:

  • Humans Only: Blocks automated bots and crawlers completely, requiring human authentication.
  • Public Web: Allows all humans and AI agents to read for free.
  • Monetized for Bots: Humans read free (or tip optionally), while AI bots and crawlers pay your default fee (e.g. 1.00 USDC) per request.
  • Paid Only: Both humans and bots must pay to unlock the full post.

Earnings accumulate in your connected account and can be settled instantly to your Base wallet or through Stripe Connect payouts.

6. Frequently Asked Questions

Why not use API keys?

API keys require humans to manually create accounts, enter credit cards, manage rate limits, and rotate tokens for every single website. Autonomous AI agents cannot negotiate individual billing terms with hundreds of thousands of independent blogs. x402 enables permissionless, machine-native commerce.

What blockchain is used?

x402 is optimized for Base, Ethereum's Layer-2 network, due to its sub-cent gas fees, rapid block confirmation times (under 2 seconds), and deep native USDC liquidity.

Does this break web search?

Publishers choose their policy. Search indexing crawlers can be allowed free passage, or charged fractional micro-fees, completely under the publisher's sovereignty.