Automate payments with the x402 client library

Automate payments with the x402 client library

The manual two-request flow (402 challenge → signed retry) is handled for you by the official x402 client SDK maintained by the x402-foundation, the same SDK family the 1inch gateway is built on. It opens the prepaid channel, signs the deposit and per-request vouchers, attaches the PAYMENT-SIGNATURE header, and resyncs automatically. You just make normal HTTP requests.

The 1inch gateway accepts only the batch-settlement scheme, so you register BatchSettlementEvmScheme.

Set depositMultiplier yourself. The SDK default of 5 escrows far less than the $2.00 minimum deposit and the channel open is rejected. The examples below target swap (GET /swap/v6.0/1/quote), whose minimum is 8334. See Sizing depositMultiplier below for other products.

TypeScript

Bash
1
npm install @x402/fetch @x402/evm @x402/core viem

Higher RPS (TypeScript)

Batch-settlement serializes one in-flight paid request per channel. Extra throughput is extra channels: register several BatchSettlementEvmScheme instances with distinct salt values. Each channel still needs a $2 deposit (five channels = $10).

Go & Python

StackBlitz cannot run Go or Python. Copy the snippet below, or run the example locally. Account setup, wallet linking, and refunds are in the x402 docs.

One channel on api.1inch.com (Base). Each deposit is $2; the SDK tops up when that balance runs out, up to $10.

GO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// go get github.com/x402-foundation/x402/go/v2 github.com/ethereum/go-ethereum

package main

import (
    "context"
    "fmt"
    "log"
    "net/http"
    "os"
    "time"

    x402 "github.com/x402-foundation/x402/go/v2"
    x402http "github.com/x402-foundation/x402/go/v2/http"
    batchedclient "github.com/x402-foundation/x402/go/v2/mechanisms/evm/batch-settlement/client"
    evmsigners "github.com/x402-foundation/x402/go/v2/signers/evm"

    "github.com/ethereum/go-ethereum/ethclient"
)

const (
    swapQuoteURL = "https://api.1inch.com/swap/v6.0/1/quote" +
        "?amount=1000000000000000000" +
        "&src=0xEeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" +
        "&dst=0xdAC17F958D2ee523a2206206994597C13D831ec7" // settlement is on Base
    depositMultiplier = 8334 // ceil($2.00 / $0.00024). SDK default (5) is rejected.
    maxDeposits       = 5    // 5 × $2 = $10 cap on auto top-ups
    paidCalls         = 10
)

func main() {
    // Base mainnet RPC. The signer reads chain state for the deposit.
    ethClient, err := ethclient.Dial("https://mainnet.base.org")
    if err != nil {
        log.Fatal(err)
    }

    // Sign with the wallet you linked to your application in the Business Portal.
    signer, err := evmsigners.NewClientSignerFromPrivateKeyWithClient(os.Getenv("EVM_PRIVATE_KEY"), ethClient)
    if err != nil {
        log.Fatal(err)
    }

    deposits := 0
    // The 1inch gateway accepts ONLY the batch-settlement scheme.
    batchScheme := batchedclient.NewBatchSettlementEvmScheme(signer, &batchedclient.BatchSettlementEvmSchemeOptions{
        DepositMultiplier: depositMultiplier,
        // Called before each on-chain deposit (open and auto top-up). Stop at $10.
        DepositStrategy: func(_ context.Context, _ batchedclient.DepositStrategyContext) (batchedclient.DepositStrategyResult, error) {
            if deposits >= maxDeposits {
                return batchedclient.DepositStrategyResult{}, fmt.Errorf("refusing deposit: would exceed $10")
            }
            return batchedclient.DepositStrategyResult{}, nil
        },
    })
    client := x402.Newx402Client().Register("eip155:*", batchScheme)
    // Count deposits so the $10 cap and the "wait for finalization" step know when one happened.
    client.OnAfterPaymentCreation(func(ctx x402.PaymentCreatedContext) error {
        if ctx.Payload != nil && ctx.Payload.GetPayload()["type"] == "deposit" {
            deposits++
        }
        return nil
    })

    // http.Client that runs 402 challenge -> deposit/voucher -> retry for you.
    httpClient := x402http.WrapHTTPClientWithPayment(
        http.DefaultClient,
        x402http.Newx402HTTPClient(client),
    )

    for call := 1; call <= paidCalls; call++ {
        before := deposits
        req, err := http.NewRequest(http.MethodGet, swapQuoteURL, nil)
        if err != nil {
            log.Fatal(err)
        }

        resp, err := httpClient.Do(req)
        if err != nil {
            log.Fatal(err)
        }
        resp.Body.Close()
        fmt.Println("call", call, "HTTP", resp.StatusCode)
        if deposits > before {
            // First call (and later top-ups) settle on-chain. Wait before the next voucher.
            time.Sleep(3 * time.Second)
        }
    }
}

Sizing depositMultiplier

The SDK sizes every deposit (the initial one and each automatic top-up) as multiplier × per-request price. It does not read the minimum deposit from the 402 challenge, so the multiplier is yours to get right. Because per-request prices are fractions of a cent, it belongs in the thousands:

Text
1
depositMultiplier >= ceil(minDeposit / per-request price)

Both inputs are advertised in the 402 challenge, in atomic USDC units (6 decimals): accepts[].maxAmountRequired is the per-request price and accepts[].extra.minDeposit.amount is the minimum prepaid deposit (2000000 = $2.00). For swap at $0.00024 per request that is ceil(2.00 / 0.00024) = 8334, i.e. a $2.00016 deposit covering 8334 paid calls.

Per-request price maxAmountRequired Minimum depositMultiplier
$0.00024 (e.g. swap) 240 8334
$0.00018 (e.g. web3) 180 11112
$0.00030 300 6667
$0.00048 480 4167

These are minimums: they escrow just above the $2.00 floor, which is also exactly the number of calls each deposit buys. Pick a larger multiplier for real volume. The same multiplier sizes every automatic top-up, so a bigger one means fewer on-chain deposits.

Good to know

  • TypeScript, Go, and Python all ship an official batch-settlement client: pick the one that matches your stack. The gateway behaves identically for all three.
  • Sign with your linked wallet: payments only succeed for a wallet linked to your application (otherwise the gateway returns 401 Wallet not linked).
  • Production runs on Base mainnet (eip155:8453). Import the base viem chain and target https://api.1inch.com.
  • depositPolicy controls how much USDC is escrowed when a channel opens, and it must clear the $2.00 minimum: depositMultiplier: 8334 for swap, not the SDK default of 5 (see Sizing depositMultiplier above). The SDK re-deposits automatically when the balance runs low.
  • The SDK handles the deposit (EIP-3009 receiveWithAuthorization), cumulative voucher signing, and corrective 402 resync after any channel state loss, with no manual header building required.

The protocol is open, so other languages can integrate directly against the x402 standard.

Did you find what you need?