Agentic payments (x402)
Let an AI agent pay for each request in USDC over x402, with no String account or API key.
String Web Access accepts the x402 protocol, so an AI agent can pay for a request by itself. It needs no String account, API key, or prepaid balance. It calls the API, gets a price back, pays in USDC, and receives the response.
x402 is for software that pays for itself. An agent working for a team that already has a String account should use an API key instead, which bills the account balance at plan rates.
How it works
x402 turns the HTTP 402 Payment Required status into a challenge and response:
┌──────────────┐ ┌────────────────────────┐
│ AI agent │ ── POST /v1/fetch ───────▶ │ request.usestring.ai │
│ (wallet │ │ │
│ with USDC) │ ◀── 402 + price ────────── │ │
│ │ │ │
│ │ ── POST /v1/fetch ───────▶ │ │
│ │ + signed payment │ │
│ │ │ │
│ │ ◀── 200 + response ─────── │ │
│ │ + receipt │ │
└──────────────┘ └────────────────────────┘- The agent sends an ordinary request with no
Authorizationheader. - String answers
402 Payment Required. ThePAYMENT-REQUIREDheader offers two ways to pay, both capped at $0.006, with the network, token, and recipient, as base64-encoded JSON. - The agent's x402 client picks an option it supports and signs a USDC authorization for it.
- The agent sends the same request again, with the signed payment in the
PAYMENT-SIGNATUREheader. - String verifies the payment and runs the request. If the request succeeds, String settles the payment and returns
the response with a
PAYMENT-RESPONSEheader, a base64-encoded receipt that includes the on-chain transaction hash.
An x402 client such as purl or @x402/fetch handles steps 2 to 4 for you.
Failed requests are not charged
String settles a payment only after the request succeeds. If the API answers with a status of 400 or above, the
payment is never submitted and no USDC leaves the wallet. An error from the site itself, such as a 409, comes back
inside a 200 and is charged like any other fetch.
Pricing
POST /v1/fetch accepts x402. The request body is the same as with an API key; see the
Fetch reference. Every other endpoint still requires an
API key, including browser sessions over /v1/wss.
The challenge lists two payment schemes, upto first:
| Scheme | What a request costs | Clients |
|---|---|---|
upto | The Starter rate for the path the fetch took, at most $0.006 | @x402/fetch with UptoEvmScheme |
exact | A flat $0.006 | Any x402 client, including purl |
Under upto, the charge depends on the fetch path and proxy class the API selects:
| Fetch path | Proxy class | Per request |
|---|---|---|
| Request-based fetch | Standard proxy | $0.0003 |
| Request-based fetch | Premium proxy | $0.003 |
| Browser-based fetch | Standard proxy | $0.0015 |
| Browser-based fetch | Premium proxy | $0.006 |
These are the Starter plan's per-request rates from Pricing.
Three fetch options need an API key, because their cost can exceed $0.006:
jsonSchema, actions, and
screenshot. A keyless request that uses one gets a 400 before any payment is
requested.
Network and token
| Setting | Value |
|---|---|
| Network | Base (eip155:8453) TBD |
| Token | USDC |
| Schemes | upto, exact |
The wallet only needs USDC. The x402 facilitator submits the transfer on-chain and pays the gas, so the agent doesn't need ETH.
Get started
Option 1: purl on the command line
purl is Stripe's curl-style client for paid HTTP requests. It pays with the exact
scheme, so each request costs a flat $0.006.
# Install purl
brew install stripe/purl/purl
# Create or import a wallet
purl wallet addFund the wallet with USDC on Base from an exchange or a bridge. Keep a separate wallet for agent spending.
# Show the price without paying
purl --dry-run --json '{"url":"https://example.com","format":"markdown"}' https://request.usestring.ai/v1/fetch
# Pay and fetch
purl --json '{"url":"https://example.com","format":"markdown"}' https://request.usestring.ai/v1/fetch--json sends the body as a POST with a JSON content type. To cap what purl will pay, add --max-amount with the
limit in USDC's smallest unit. USDC has six decimals, so --max-amount 6000 refuses any price above $0.006.
purl balance shows what is left in the wallet.
Option 2: TypeScript with @x402/fetch
@x402/fetch wraps fetch, so a 402 is paid and the request retried without extra code. Register the upto scheme
to pay the actual Starter rate. It reads the wallet's token approvals over RPC, so give it a Base RPC URL.
npm install @x402/fetch @x402/evm viemimport { decodePaymentResponseHeader, wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { UptoEvmScheme } from "@x402/evm/upto/client";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as `0x${string}`);
const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
schemes: [
{
network: "eip155:8453",
client: new UptoEvmScheme(account, { rpcUrl: "https://mainnet.base.org" })
}
]
});
const response = await fetchWithPayment("https://request.usestring.ai/v1/fetch", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ url: "https://example.com", format: "markdown" })
});
console.log(await response.text());
const receipt = response.headers.get("PAYMENT-RESPONSE");
if (receipt) console.log(decodePaymentResponseHeader(receipt));To pay the flat $0.006 instead, register ExactEvmScheme from @x402/evm. Load the private key from the environment,
and never commit it.
Responses
| Status | Header | Meaning |
|---|---|---|
402 | PAYMENT-REQUIRED | No payment was attached, or the payment was rejected, for example because the wallet holds too little USDC. The header carries the price options and the reason; pay again. |
402 | PAYMENT-RESPONSE | The payment was accepted but failed to settle on-chain. The receipt in the header has success: false and the reason. |
200 | PAYMENT-RESPONSE | Paid and served. The header is the settlement receipt. |
400 | — | Invalid input, or jsonSchema, actions, or screenshot sent without an API key. No payment is requested. |
403 | — | The destination or path needs approval. The reason says to create a String account and complete KYC. Nothing is charged. |
429 | Retry-After | A rate limit was reached; see Notes. |
| Any other status of 400 or more | — | The request failed and nothing was charged. See Errors. |
Notes
- Each request is paid on its own. There is no balance, so nothing carries over from one call to the next.
- A request that sends an
Authorizationheader is billed to its account as usual and never goes through x402. - Without a payment, each IP gets 5 requests a minute, and failed payments count toward that. Paid requests are limited to 10 a second per wallet.
- Captcha solving is off for x402 requests. A page that needs one fails, and nothing is charged.
Claude skill
Add the skill below to Claude Code. When you ask Claude to read a URL, it fetches the page through String and pays for it over x402.
Add SKILL.md
Save the contents below as .claude/skills/string-x402/SKILL.md.
Set up purl
Claude pays through purl, so install it and fund a wallet as in
Option 1.
Ask Claude to fetch a page
Claude Code loads the skill on its own. Ask it to read a URL, and it calls String through purl.
---
name: string-x402
description: Fetch web pages through String Web Access and pay for each request with x402 (USDC on Base) using purl. Use when asked to read, fetch, or scrape a URL, especially a site that blocks bots.
---
# String Web Access over x402
String fetches any URL, including sites behind bot protection or JavaScript rendering, and
returns it as Markdown or JSON. Each request is paid with x402 through `purl`, so no API key is
needed.
## Endpoint
`POST https://request.usestring.ai/v1/fetch`, $0.006 per request through `purl`.
Body: `{"url": "https://example.com", "format": "markdown"}`
- `format`: `markdown` is best for reading; `json` (the default) and `raw` are also available.
- `mainContentOnly: true` strips navigation, headers, and footers from Markdown.
- `executeJS: true` renders the page in a browser first.
- `jsonSchema`, `actions`, and `screenshot` need an API key. Don't send them.
All parameters: https://portal.usestring.ai/docs/api-reference/fetch
## Calling it
```bash
purl --json '{"url":"https://example.com","format":"markdown"}' https://request.usestring.ai/v1/fetch
```
- Add `--dry-run` to see the price without paying.
- Add `--max-amount 6000` to refuse anything above $0.006. USDC has six decimals.
- A `402` after paying means the payment failed. Check the wallet with `purl balance`.
- A `403` means the site needs an approved String account. Tell the user rather than retrying.
- Failed requests, with a status of 400 or above, are not charged.
## Cost and etiquette
Every successful fetch is paid. Fetch the page you need rather than crawling around it, and don't
re-fetch a URL you already have in context.