catalogue / guides / How AI agents pay for APIs with x402

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

EndpointPriceTimeoutmax_timeout_secondsSize capMax duration
archive-extract-file$0.00390 s11125 MB (26,214,400 bytes)-
archive-inspect$0.00360 s81100 MB (104,857,600 bytes)-
audio-convert$0.00394 s115100 MB (104,857,600 bytes)60 min (3,600 s)
audio-extract$0.00394 s115200 MB (209,715,200 bytes)60 min (3,600 s)
color-palette-extract$0.00360 s8125 MB (26,214,400 bytes)-
csv-to-json$0.00530 s5125 MB (26,214,400 bytes)-
dns-lookupfree5 s62 MB (2,097,152 bytes)-
docx-to-markdown$0.00540 s6125 MB (26,214,400 bytes)-
email-validate$0.0025 s262 MB (2,097,152 bytes)-
epub-to-text$0.00545 s6625 MB (26,214,400 bytes)-
favicon-extract$0.00345 s6625 MB (26,214,400 bytes)-
feed-discover$0.0028 s295 MB (5,242,880 bytes)-
file-type-detect$0.00330 s5132 MB (33,554,432 bytes)-
html-clean-text$0.00212 s331 MB (1,048,576 bytes)-
html-to-pdf$0.00512 s335 MB (5,242,880 bytes)-
http-headers$0.0028 s2964 KB (65,536 bytes)-
image-compress$0.00360 s8125 MB (26,214,400 bytes)-
image-convert$0.00360 s8125 MB (26,214,400 bytes)-
image-metadata$0.00330 s5125 MB (26,214,400 bytes)-
image-probe$0.00320 s4125 MB (26,214,400 bytes)-
image-resize$0.00360 s8125 MB (26,214,400 bytes)-
ip-info$0.0025 s262 MB (2,097,152 bytes)-
json-to-csv$0.00530 s5125 MB (26,214,400 bytes)-
link-extract$0.0028 s295 MB (5,242,880 bytes)-
markdown-to-pdf$0.00512 s332 MB (2,097,152 bytes)-
media-probe$0.00330 s51100 MB (104,857,600 bytes)-
ocr-image-to-text$0.00545 s6610 MB (10,485,760 bytes)-
page-metadata$0.0028 s293 MB (3,145,728 bytes)-
pdf-merge$0.00545 s668 MB (8,388,608 bytes)-
pdf-metadata$0.00525 s4625 MB (26,214,400 bytes)-
pdf-split$0.00530 s5125 MB (26,214,400 bytes)-
pdf-to-markdown$0.00540 s6125 MB (26,214,400 bytes)-
pdf-to-text$0.00540 s6125 MB (26,214,400 bytes)-
pptx-to-text$0.00540 s6125 MB (26,214,400 bytes)-
remote-file-hash$0.00360 s81100 MB (104,857,600 bytes)-
robots-check$0.0028 s29500 KB (512,000 bytes)-
rss-parse$0.0028 s295 MB (5,242,880 bytes)-
sitemap-parse$0.00214 s3510 MB (10,485,760 bytes)-
ssl-cert-info$0.00210 s312 MB (2,097,152 bytes)-
structured-data-extract$0.0028 s295 MB (5,242,880 bytes)-
subtitle-convert$0.00315 s365 MB (5,242,880 bytes)-
subtitle-extract$0.00360 s81200 MB (209,715,200 bytes)-
svg-to-png$0.00360 s8125 MB (26,214,400 bytes)-
url-to-markdown$0.00212 s331 MB (1,048,576 bytes)-
url-unshorten$0.00210 s3164 KB (65,536 bytes)-
video-thumbnail$0.00360 s81200 MB (209,715,200 bytes)-
waveform-data$0.00390 s111100 MB (104,857,600 bytes)-
wellknown-fetch$0.00212 s332 MB (2,097,152 bytes)-
whois$0.00212 s332 MB (2,097,152 bytes)-
xlsx-to-json$0.00540 s6125 MB (26,214,400 bytes)-

06Limits

07Pay for an API call with x402

  1. Discover the endpoint

    Read /.well-known/x402 and the free GET /v1/<name>/schema response. Prepare a JSON body matching the input schema.

  2. Request the payment requirements

    POST the JSON body to the paid endpoint with content-type: application/json and without a payment header.

  3. Inspect the challenge

    Read HTTP 402 and inspect accepts for network, asset, amount, payTo and maxTimeoutSeconds before authorizing payment.

  4. Sign the authorization

    Create the EIP-712 signed EIP-3009 authorization matching the requirements, with validity covering the whole-call allowance. Retain its nonce.

  5. Resubmit with payment

    Base64-encode the JSON PaymentPayload and resend the request with that value in payment-signature.

  6. Record the result

    On HTTP 200, retain the output and payment-response receipt. 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
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"
      }
    }
  ]
}
HTTP request
POST /v1/page-metadata
content-type: application/json
payment-signature: <base64 x402 payload>

{
  "url": "https://grist.tools/samples/web/render-article.html"
}
HTTP response
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>/schema is free. The agent supplies a signed payment authorization when submitting the paid call.
Which header carries the signed payment?
Send base64-encoded JSON PaymentPayload in payment-signature. The older x-payment header is accepted, but payment-signature wins if both are present. Read payment-required for the challenge and payment-response for 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_input errors 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 through authorizationState.
Can I reuse a payment authorization for another call?
An authorization nonce can be settled only once. replay_in_flight means wait without paying again, while replay_consumed means it was used and may have settled. Check its on-chain authorizationState when 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 as max_timeout_seconds in metadata. It includes verification and settlement around the handler. The validity guidance is validBefore = now + max_timeout_seconds.
Can I use a client library for the payment flow?
The endpoint documentation provides JavaScript examples using wrapFetchWithPayment and ExactEvmScheme, plus decodePaymentResponseHeader for the receipt. Python examples use x402ClientSync, register_exact_evm_client, EthAccountSigner and x402_requests(client). The curl examples show the unpaid request followed by resubmission with payment-signature.

14Related pages