How AI agents pay for APIs with x402
An AI agent pays for a paid API call by reading the HTTP 402 requirements, signing a payment authorization and resending the request with payment-signature. On grist.tools, payment uses USDC on Base without an account or API key, with verification before execution and settlement only after the handler succeeds.
Updated
01Discover the endpoint before authorizing payment
Start with /.well-known/x402, which lists endpoints with their payment requirements and metadata, including name, description, family, price, free status, deadline, schema URL and documentation URL. The catalogue covers 50 endpoints. You can also discover services through /openapi.json, /llms.txt, /llms-full.txt and /index.json.
Read GET /v1/<name>/schema before constructing the body. This free response includes the input schema, input and output examples, errors, price and timeouts. Use it to select the endpoint and prepare valid JSON before signing. The table below compares declared service prices and limits and links to each endpoint contract.
Free endpoints take a plain call without verification or settlement. For example, the price of dns-lookup is free, and its manifest entry has an empty accepts array. Do not require a payment challenge on that path.
Budget each successful extraction separately when processing several pages through url-to-markdown. Its per-call price is $0.002; read each additional endpoint contract before adding another operation.
02Read the unpaid response as payment requirements
Send POST https://grist.tools/v1/url-to-markdown with a JSON body and content-type: application/json. Without a payment header, a paid endpoint returns HTTP 402 after checking the request body size. This happens before schema validation, so the challenge does not confirm that your input is valid.
The body contains x402Version, error, resource and accepts. The resource describes the requested URL, service, response type and family; accepts supplies payment requirements. The payment-required response header contains the same envelope as base64-encoded JSON. The generated example below uses page-metadata and shows its envelope alongside the utility request and response.
The requirements use x402 version 2 and the exact scheme. Inspect network, asset, amount, payTo and maxTimeoutSeconds. Payment runs on Base; the price of url-to-markdown is $0.002. Read the atomic-unit amount from the requirements when preparing payment.
03Sign an authorization that matches the challenge
The payment payload carries an EIP-3009 transferWithAuthorization, signed using EIP-712. Its fields include from, to, value, nonce and validBefore. The recipient and value are checked against payTo and amount. Use the advertised requirements to construct the authorization, and keep its nonce available for settlement checks.
Encode the JSON PaymentPayload as base64 and send it in payment-signature. The older x-payment header remains accepted as an alias; if both headers are present, payment-signature takes precedence. That alias does not remove payload checks: malformed payloads, unsupported versions, mismatched requirements and invalid validity windows receive HTTP 402 with a reason in error.
In the JavaScript documentation, x402Client registers the advertised network with ExactEvmScheme(account), and wrapFetchWithPayment supplies the payment wrapper. Use the endpoint example when connecting these pieces. Keep the advertised recipient, amount and deadline visible in your integration so you can compare them with a refused authorization.
04Submit the paid request and retain the receipt
Resend the JSON request with the payment header. The server checks the body against the schema before payment verification. After verification come the payer limit, replay reservation and destination-domain limit, followed by the handler. A facilitator verifies the payment before work and settles it after handler success. A verification result of isValid: false returns HTTP 402 with the reason, before the handler runs.
A successful paid response returns HTTP 200, the utility output and a receipt in payment-response. The x-payment-response alias carries the same value. Retain the receipt with the result when recording a completed call. On /v1/*, CORS exposes payment-required, payment-response and x-payment-response, allowing browser clients to read those response headers.
05Services covered
| Endpoint | Price | Timeout | max_timeout_seconds | Size cap | Max duration |
|---|---|---|---|---|---|
| archive-extract-file | $0.003 | 90 s | 111 | 25 MB (26,214,400 bytes) | - |
| archive-inspect | $0.003 | 60 s | 81 | 100 MB (104,857,600 bytes) | - |
| audio-convert | $0.003 | 94 s | 115 | 100 MB (104,857,600 bytes) | 60 min (3,600 s) |
| audio-extract | $0.003 | 94 s | 115 | 200 MB (209,715,200 bytes) | 60 min (3,600 s) |
| color-palette-extract | $0.003 | 60 s | 81 | 25 MB (26,214,400 bytes) | - |
| csv-to-json | $0.005 | 30 s | 51 | 25 MB (26,214,400 bytes) | - |
| dns-lookup | free | 5 s | 6 | 2 MB (2,097,152 bytes) | - |
| docx-to-markdown | $0.005 | 40 s | 61 | 25 MB (26,214,400 bytes) | - |
| email-validate | $0.002 | 5 s | 26 | 2 MB (2,097,152 bytes) | - |
| epub-to-text | $0.005 | 45 s | 66 | 25 MB (26,214,400 bytes) | - |
| favicon-extract | $0.003 | 45 s | 66 | 25 MB (26,214,400 bytes) | - |
| feed-discover | $0.002 | 8 s | 29 | 5 MB (5,242,880 bytes) | - |
| file-type-detect | $0.003 | 30 s | 51 | 32 MB (33,554,432 bytes) | - |
| html-clean-text | $0.002 | 12 s | 33 | 1 MB (1,048,576 bytes) | - |
| html-to-pdf | $0.005 | 12 s | 33 | 5 MB (5,242,880 bytes) | - |
| http-headers | $0.002 | 8 s | 29 | 64 KB (65,536 bytes) | - |
| image-compress | $0.003 | 60 s | 81 | 25 MB (26,214,400 bytes) | - |
| image-convert | $0.003 | 60 s | 81 | 25 MB (26,214,400 bytes) | - |
| image-metadata | $0.003 | 30 s | 51 | 25 MB (26,214,400 bytes) | - |
| image-probe | $0.003 | 20 s | 41 | 25 MB (26,214,400 bytes) | - |
| image-resize | $0.003 | 60 s | 81 | 25 MB (26,214,400 bytes) | - |
| ip-info | $0.002 | 5 s | 26 | 2 MB (2,097,152 bytes) | - |
| json-to-csv | $0.005 | 30 s | 51 | 25 MB (26,214,400 bytes) | - |
| link-extract | $0.002 | 8 s | 29 | 5 MB (5,242,880 bytes) | - |
| markdown-to-pdf | $0.005 | 12 s | 33 | 2 MB (2,097,152 bytes) | - |
| media-probe | $0.003 | 30 s | 51 | 100 MB (104,857,600 bytes) | - |
| ocr-image-to-text | $0.005 | 45 s | 66 | 10 MB (10,485,760 bytes) | - |
| page-metadata | $0.002 | 8 s | 29 | 3 MB (3,145,728 bytes) | - |
| pdf-merge | $0.005 | 45 s | 66 | 8 MB (8,388,608 bytes) | - |
| pdf-metadata | $0.005 | 25 s | 46 | 25 MB (26,214,400 bytes) | - |
| pdf-split | $0.005 | 30 s | 51 | 25 MB (26,214,400 bytes) | - |
| pdf-to-markdown | $0.005 | 40 s | 61 | 25 MB (26,214,400 bytes) | - |
| pdf-to-text | $0.005 | 40 s | 61 | 25 MB (26,214,400 bytes) | - |
| pptx-to-text | $0.005 | 40 s | 61 | 25 MB (26,214,400 bytes) | - |
| remote-file-hash | $0.003 | 60 s | 81 | 100 MB (104,857,600 bytes) | - |
| robots-check | $0.002 | 8 s | 29 | 500 KB (512,000 bytes) | - |
| rss-parse | $0.002 | 8 s | 29 | 5 MB (5,242,880 bytes) | - |
| sitemap-parse | $0.002 | 14 s | 35 | 10 MB (10,485,760 bytes) | - |
| ssl-cert-info | $0.002 | 10 s | 31 | 2 MB (2,097,152 bytes) | - |
| structured-data-extract | $0.002 | 8 s | 29 | 5 MB (5,242,880 bytes) | - |
| subtitle-convert | $0.003 | 15 s | 36 | 5 MB (5,242,880 bytes) | - |
| subtitle-extract | $0.003 | 60 s | 81 | 200 MB (209,715,200 bytes) | - |
| svg-to-png | $0.003 | 60 s | 81 | 25 MB (26,214,400 bytes) | - |
| url-to-markdown | $0.002 | 12 s | 33 | 1 MB (1,048,576 bytes) | - |
| url-unshorten | $0.002 | 10 s | 31 | 64 KB (65,536 bytes) | - |
| video-thumbnail | $0.003 | 60 s | 81 | 200 MB (209,715,200 bytes) | - |
| waveform-data | $0.003 | 90 s | 111 | 100 MB (104,857,600 bytes) | - |
| wellknown-fetch | $0.002 | 12 s | 33 | 2 MB (2,097,152 bytes) | - |
| whois | $0.002 | 12 s | 33 | 2 MB (2,097,152 bytes) | - |
| xlsx-to-json | $0.005 | 40 s | 61 | 25 MB (26,214,400 bytes) | - |
06Limits
- Largest response cap: 200 MB (209,715,200 bytes) on audio-extract.
- Smallest response cap: 64 KB (65,536 bytes) on http-headers.
- Longest timeout: 94 s on audio-convert.
- Shortest timeout: 5 s on dns-lookup.
- max_timeout_seconds: from 6 to 115.
- Max input duration on audio-convert: 60 min (3,600 s).
- Max input duration on audio-extract: 60 min (3,600 s).
07Pay for an API call with x402
- Discover the endpoint
Read
/.well-known/x402and the freeGET /v1/<name>/schemaresponse. Prepare a JSON body matching the input schema. - Request the payment requirements
POST the JSON body to the paid endpoint with
content-type: application/jsonand without a payment header. - Inspect the challenge
Read HTTP 402 and inspect
acceptsfornetwork,asset,amount,payToandmaxTimeoutSecondsbefore authorizing payment. - Sign the authorization
Create the EIP-712 signed EIP-3009 authorization matching the requirements, with validity covering the whole-call allowance. Retain its nonce.
- Resubmit with payment
Base64-encode the JSON
PaymentPayloadand resend the request with that value inpayment-signature. - Record the result
On HTTP 200, retain the output and
payment-responsereceipt. Otherwise, inspect the error and any settlement uncertainty before retrying.
08End-to-end example
A real recorded example of page-metadata, the same one its docs page publishes. Strings in the response longer than 400 characters are cut and end with ....
402 Payment Required
payment-required: <base64 of the JSON below>
{
"x402Version": 2,
"error": "payment required",
"resource": {
"url": "https://grist.tools/v1/page-metadata",
"description": "Fetches a page and returns its <head> metadata -- title, description, canonical, language, hreflang, favicon, Open Graph and Twitter cards -- with every URL resolved to absolute.",
"mimeType": "application/json",
"serviceName": "grist.tools",
"tags": [
"fetch"
]
},
"accepts": [
{
"scheme": "exact",
"network": "eip155:8453",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"amount": "2000",
"payTo": "0x2C5d7FAe8338e5472755a9B9cafcDEad90a8F81C",
"maxTimeoutSeconds": 29,
"extra": {
"name": "USD Coin",
"version": "2"
}
}
]
}POST /v1/page-metadata
content-type: application/json
payment-signature: <base64 x402 payload>
{
"url": "https://grist.tools/samples/web/render-article.html"
}200 OK
payment-response: <base64 settlement receipt>
{
"url": "https://grist.tools/samples/web/render-article.html",
"source_url": "https://grist.tools/samples/web/render-article.html",
"final_url": "https://grist.tools/samples/web/render-article.html",
"http_status": 200,
"title": "Measuring Rainfall with a Simple Gauge",
"description": "A short guide to reading a cylinder rain gauge at the same hour every day.",
"canonical": "https://grist.tools/samples/web/render-article.html",
"lang": "en",
"hreflang": [
{
"lang": "en",
"href": "https://grist.tools/samples/web/render-article.html"
},
{
"lang": "x-default",
"href": "https://grist.tools/samples/web/render-article.html"
}
],
"hreflang_total": 2,
"hreflang_truncated": false,
"favicon": "https://grist.tools/favicon.png",
"charset": "utf-8",
"opengraph": {
"title": "Measuring Rainfall with a Simple Gauge",
"description": "Read the gauge at eye level, at the same hour, and write the number down.",
"image": "https://grist.tools/og.png",
"type": "article",
"site_name": "Grist Samples",
"url": "https://grist.tools/samples/web/render-article.html"
},
"twitter": {
"card": "summary_large_image",
"title": "Measuring Rainfall with a Simple Gauge",
"description": "Read the gauge at eye level, at the same hour, and write the number down.",
"image": "https://grist.tools/og.png"
},
"robots_meta": "index, follow",
"fetched_at": "2026-09-01T12:00:00.000Z"
}09Use the whole-call deadline for payment validity
The handler timeout for url-to-markdown is 12 s. Its whole-call allowance is 33 seconds, published as max_timeout_seconds in discovery metadata and maxTimeoutSeconds in the payment requirements. That allowance includes verification and settlement around the handler. Size the client deadline around the whole call.
The authorization uses validBefore in Unix seconds. Its remaining validity must cover the whole-call allowance, subject to a small tolerance. Too little remaining time produces authorization_validity_too_short; a deadline in the past produces authorization_expired. An excessively distant deadline produces authorization_validity_too_long.
When correcting the validity window, follow the instruction to sign again with validBefore = now + max_timeout_seconds. Check the requirements for the endpoint you are calling instead of carrying a handler timeout from another endpoint into the authorization.
10Distinguish execution failure from uncertain settlement
Verification does not settle the payment. Gate refusals and handler failures are never settled, and a handler failure returns its typed error without a charge. If the client disconnects before settlement, settlement is skipped. These rules make the point of failure relevant when interpreting an unsuccessful call.
If the handler succeeds but the settle call fails without an answer, or returns a tx_timeout that does not qualify for delivery (no usable transaction hash, a different network, or a payer or amount mismatch), the response is HTTP 500 internal with the message "The work was done but settlement could not be completed". A facilitator answer that fails validation also returns HTTP 500 internal, with a message starting "Payment settlement returned". In these cases the payment outcome is uncertain and the output is not delivered. Preserve the message and check the on-chain EIP-3009 authorizationState for the nonce before signing a new payment.
A tx_timeout with a well-formed transaction hash on the charged network and no payer or amount mismatch returns HTTP 200 with the utility output and a payment-response receipt whose success is false; the transfer is in flight and may still revert. Any other failed settlement (a reason other than tx_timeout) returns HTTP 402 with the reason in the error field, the payment-response header and no utility output. Read the reason rather than treating every 402 as an initial request for payment. Keep the response status, error and nonce together when investigating a failed attempt.
11Handle replay responses before signing again
Each authorization is tracked by its nonce, and the EIP-3009 nonce can be settled only once. Reusing an authorization can therefore produce a replay error. Use the reason to decide what happens next; automatically signing again on every payment error ignores whether earlier work is still running or may already have settled.
replay_in_flight means the first call is still running: wait and do not pay again. replay_consumed means the authorization was already used and may have been settled; another call requires a new payment. replay_exhausted means the authorization has reached its attempt limit.
To determine whether the authorization was settled, read the on-chain EIP-3009 authorizationState for its nonce. Use that check when a consumed authorization or uncertain settlement leaves the result unclear. Retaining the nonce gives your retry handling a reference to the payment whose outcome you need to establish.
12Correct request failures and respect retry signals
Utility calls require POST; other methods receive HTTP 405 method_not_allowed. A content type other than application/json receives HTTP 415 before payment. An oversized request body receives HTTP 413 too_large, with the cap in details.limit_bytes, also before payment. Schema failure with a payment header returns HTTP 400 invalid_input before verification.
HTTP 429 rate_limited indicates too many calls from a payer or toward a destination domain. Nothing is charged; use retry-after to schedule a later attempt. Retryable HTTP 503 errors, including concurrency_limit, registry_at_capacity and upstream_unavailable, also require waiting for the interval in that header. When upstream_unavailable comes from the front proxy, the call may already have run and settled; check authorizationState before paying again.
For handler errors, use the typed code to identify the cause. blocked_target means the network guard refused the destination; unreachable_target means resolution or connection failed. upstream_timeout means the call exceeded the declared handler timeout of the endpoint, while unprocessable means the received bytes could not be processed. Correct the source or request where appropriate before retrying.
unsupported_content_type means the destination returned an unsupported type. too_large can identify a fetched response exceeding the streaming size cap or allowed decompression ratio. upstream_status means the destination answered with a status the endpoint cannot use.
13Questions
- Do I need an account or API key to pay?
- No account or API key is required. A paid endpoint advertises its requirements in HTTP 402 before payment, and
GET /v1/<name>/schemais free. The agent supplies a signed payment authorization when submitting the paid call. - Which header carries the signed payment?
- Send base64-encoded JSON
PaymentPayloadinpayment-signature. The olderx-paymentheader is accepted, butpayment-signaturewins if both are present. Readpayment-requiredfor the challenge andpayment-responsefor the successful payment receipt. - Does receiving HTTP 402 mean my input passed validation?
- No; an unpaid paid-endpoint request receives its challenge after the body size check and before schema validation. With a payment header, the body is validated before verification. Read the free schema and correct
invalid_inputerrors before resubmitting. - Am I charged when the handler fails?
- No; handler failures are never settled, and payment verification happens before execution. A settlement request that fails without an answer or a settlement answer that fails validation (that case is HTTP 500
internal, with a message starting "Payment settlement returned") is a separate case: the work completed, but the payment outcome is uncertain. Preserve that error and check the nonce throughauthorizationState. - Can I reuse a payment authorization for another call?
- An authorization nonce can be settled only once.
replay_in_flightmeans wait without paying again, whilereplay_consumedmeans it was used and may have settled. Check its on-chainauthorizationStatewhen the outcome is unclear before signing a new payment. - Which timeout belongs in my authorization?
- Use the whole-call allowance advertised in the payment requirements as
maxTimeoutSeconds, also published asmax_timeout_secondsin metadata. It includes verification and settlement around the handler. The validity guidance isvalidBefore = now + max_timeout_seconds. - Can I use a client library for the payment flow?
- The endpoint documentation provides JavaScript examples using
wrapFetchWithPaymentandExactEvmScheme, plusdecodePaymentResponseHeaderfor the receipt. Python examples usex402ClientSync,register_exact_evm_client,EthAccountSignerandx402_requests(client). The curl examples show the unpaid request followed by resubmission withpayment-signature.
14Related pages
- Archive Extract File API docs
- Archive Inspect API docs
- Audio Convert API docs
- Audio Extract API docs
- Color Palette Extract API docs
- CSV to JSON API docs
- DNS Lookup API docs
- DOCX to Markdown API docs
- Email Validate API docs
- EPUB to Text API docs
- Favicon Extract API docs
- Feed Discover API docs
- File Type Detect API docs
- HTML Clean Text API docs
- HTML to PDF API docs
- HTTP Headers API docs
- Image Compress API docs
- Image Convert API docs
- Image Metadata API docs
- Image Probe API docs
- Image Resize API docs
- IP Info API docs
- JSON to CSV API docs
- Link Extract API docs
- Markdown to PDF API docs
- Media Probe API docs
- OCR Image to Text API docs
- Page Metadata API docs
- PDF Merge API docs
- PDF Metadata API docs
- PDF Split API docs
- PDF to Markdown API docs
- PDF to Text API docs
- PPTX to Text API docs
- Remote File Hash API docs
- Robots Check API docs
- RSS Parse API docs
- Sitemap Parse API docs
- SSL Cert Info API docs
- Structured Data Extract API docs
- Subtitle Convert API docs
- Subtitle Extract API docs
- SVG to PNG API docs
- URL to Markdown API docs
- URL Unshorten API docs
- Video Thumbnail API docs
- Waveform Data API docs
- Well-Known Fetch API docs
- Whois API docs
- XLSX to JSON API docs
- Archive Extract File API
- Archive Inspect API
- Audio Convert API
- Audio Extract API
- Color Palette Extract API
- CSV to JSON API
- DNS Lookup API
- DOCX to Markdown API
- Email Validate API
- EPUB to Text API
- Favicon Extract API
- Feed Discover API
- File Type Detect API
- HTML Clean Text API
- HTML to PDF API
- HTTP Headers API
- Image Compress API
- Image Convert API
- Image Metadata API
- Image Probe API
- Image Resize API
- IP Info API
- JSON to CSV API
- Link Extract API
- Markdown to PDF API
- Media Probe API
- OCR Image to Text API
- Page Metadata API
- PDF Merge API
- PDF Metadata API
- PDF Split API
- PDF to Markdown API
- PDF to Text API
- PPTX to Text API
- Remote File Hash API
- Robots Check API
- RSS Parse API
- Sitemap Parse API
- SSL Cert Info API
- Structured Data Extract API
- Subtitle Convert API
- Subtitle Extract API
- SVG to PNG API
- URL to Markdown API
- URL Unshorten API
- Video Thumbnail API
- Waveform Data API
- Well-Known Fetch API
- Whois API
- XLSX to JSON API
- Fetch & extract
- Feeds & site structure
- Domain, DNS & network
- Documents
- Images
- Audio & video
- Archives & binary
- API documentation
- Guides
- Turning web pages into LLM-ready text
- Parsing PDF, DOCX, XLSX and EPUB for agents
- Audio and video conversion API without ffmpeg servers