MPP

Pay per request in USDC on Tempo.
View as MarkdownOpen in Claude

The Machine Payments Protocol (MPP) is an open standard for machine-to-machine payments over HTTP, co-authored by Tempo and Stripe and specified as an IETF draft at paymentauth.org.

Like x402, MPP uses the HTTP 402 Payment Required status code. It adds payment-method neutrality: one client integration can settle in a stablecoin, on a card, or in fiat, and the wire format does not change. The handshake is Challenge, then Credential, then Receipt, built on the standard HTTP authentication headers.

The You.com Web Search and Finance Research APIs accept MPP. A client can call either one with no API key and no You.com account.

Web Search costs $0.01 per call and Finance Research $0.11 or $0.50 depending on the effort tier. None of it draws down your credit balance. If your wallet is on Base or Solana rather than Tempo, x402 is half the price on Web Search and identical on Finance Research.

The only method we currently advertise is tempo, settling in USDC. Cards and fiat are supported by the protocol but are not enabled on this endpoint yet. If you need to pay by card, use an API key with credits.


How It Works

  1. The client makes a normal request. No X-API-Key, no Authorization header.
  2. You.com responds 402 Payment Required with a Challenge in the WWW-Authenticate header, using the Payment authentication scheme. The Challenge names the price, the payment method, the intent, and when the offer expires.
  3. The client fulfills the payment by signing a transaction on the named chain.
  4. The client retries the same request with a Credential in the Authorization header. The Credential echoes the Challenge and carries method-specific proof of payment.
  5. You.com verifies the Credential, runs the request, and responds 200 OK with a Receipt in the Payment-Receipt header.

Headers

HeaderDirectionContents
WWW-AuthenticateYou.com → clientChallenge, using the Payment scheme
AuthorizationClient → You.comCredential, using the Payment scheme
Payment-ReceiptYou.com → clientBase64url-encoded Receipt

Supported Endpoints

EndpointPrice per callAmount in token base units
GET /v1/search$0.0110000
POST /v1/finance_research (deep)$0.11110000
POST /v1/finance_research (exhaustive)$0.50500000

MPP amounts are expressed in the base units of the token named by currency. USDC has six decimals, so 10000 is $0.01.

The HTTP method differs per endpoint, and the wrong one returns 401 with no Challenge rather than a helpful error. Web Search offers payment on GET only, and Finance Research on POST only. POST /v1/search and GET /v1/finance_research both return 401.

The Contents and Research APIs return 401 on every method and do not offer payment terms.

One cent is a floor rather than a coincidence. MPP settles through Stripe, and one cent is the smallest amount a Stripe-hosted payment can charge, so the per-call price is exactly one cent. x402 has no such minimum and charges $0.005 for the same call.

Query parameters and the response body are the same as the key-authenticated GET endpoint. See the Web Search API reference. Result count is count, and an unrecognized parameter is ignored rather than rejected, so a misspelled one silently returns defaults.

livecrawl is supported and priced into the Challenge. It adds $0.001 per result to the underlying price, and MPP then rounds up to the next whole cent:

RequestChallenge amountBase units
?query=...&count=10$0.0110000
?query=...&count=10&livecrawl=web$0.0220000
?query=...&count=50&livecrawl=web$0.0660000

count sets how many pages are charged for, and the crawl mode does not change the price. Because of the cent rounding, small count values absorb into the base price: count=5&livecrawl=web still quotes 10000.

If you are comparing costs against a key-authenticated integration, machine payments charge per count regardless of crawl mode, where credit billing charges per page fetched. At count=10 with livecrawl=all that is 10 pages here and 20 there. Budget from the amount in the Challenge, which is what you actually pay on this path.

Read the amount out of the Challenge rather than computing it. A Challenge issued for one count and livecrawl combination does not carry over to a different one.

Finance Research

Finance Research takes a JSON body on POST, so there is no query-string form. The financial question goes in input, and research_effort selects the tier:

$curl -X POST https://api.you.com/v1/finance_research \
> -H "Content-Type: application/json" \
> -d '{"input": "NVDA Q2 guidance vs consensus", "research_effort": "deep"}'

The price is fixed per tier rather than scaling with the request, and deep is the default when research_effort is omitted. Both tiers land on a whole cent already, so the rounding above does not apply and MPP costs the same as x402 here. See the Finance Research API reference for the response shape.

Send deep or exhaustive and nothing else. Those are the only values the Finance Research API reference accepts, and it returns 422 for anything else. The Challenge is issued before the body is validated, so it will quote a price for an unsupported tier rather than refusing it. Paying against that quote risks spending on a request the endpoint will reject.

Latency is the other thing to plan for. A deep call takes under 120 seconds and exhaustive under 300, well beyond a typical HTTP client default, so raise your timeout before the payment round trip rather than after.


Payment Methods and Intents

methodSettles throughNotes
tempoTempo L1, USDCThe only method currently advertised.
intentSupportedBehavior
chargeYesOne payment per request. The only intent we advertise.
sessionNoPay-as-you-go across many requests, settled in aggregate. On the roadmap.
subscriptionNoNot applicable to per-query search. Use an API key and credits instead.

Reading the Challenge

402 Payment Required
1HTTP/1.1 402 Payment Required
2WWW-Authenticate: Payment id="IptU2AvkLwJDGuVYmu6UADhNiPvcPb1Ln5fmC54lswU",
3 realm="api.you.com",
4 method="tempo",
5 intent="charge",
6 description="You.com pay-per-call (Tempo USDC)",
7 expires="2026-08-03T14:24:11Z",
8 request="eyJhbW91bnQiOiIxMDAwMCIsImN1cnJlbmN5IjoiMHgyMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDBiOTUzN2QxMWM2MGU4YjUwIiwibWV0aG9kRGV0YWlscyI6eyJjaGFpbklkIjo0MjE3fSwicmVjaXBpZW50IjoiMHguLi4ifQ"
ParameterMeaning
idUnique challenge identifier. Echo it back in the Credential.
realmProtection space, per RFC 9110. Always api.you.com.
methodPayment method the Challenge applies to. Currently always tempo.
intentCharge shape. Currently always charge.
descriptionHuman-readable description of what is being paid for.
expiresISO 8601 expiry, 10 minutes after issue. Pay and retry before this timestamp.
requestBase64url-encoded JSON with the method-specific payment details.

The spec also defines optional opaque and digest parameters. Neither is currently emitted, so there is nothing extra to echo back and no integrity digest to verify.

The decoded request object:

1{
2 "amount": "10000",
3 "currency": "0x20c000000000000000000000b9537d11c60e8b50",
4 "methodDetails": {
5 "chainId": 4217
6 },
7 "recipient": "0x..."
8}
FieldMeaning
amountPrice in the token’s base units. 10000 is $0.01 at six decimals.
currencyContract address of the token to pay in, not a fiat currency code.
methodDetails.chainIdChain to settle on. 4217 is Tempo.
recipientThe You.com receiving address.

The recipient value is redacted above, and it changes between challenges. Take it from the Challenge you were issued rather than from these docs or from a previous response. A client that caches a recipient will pay the wrong address.

Do not modify a Challenge and resubmit it. Pass the parameters back exactly as they arrived, including the request string, which must be echoed byte for byte rather than decoded and re-encoded.


Submitting a Credential

1Authorization: Payment eyJjaGFsbGVuZ2UiOnsiaWQiOiJJcHRVMkF2a0x3SkRHdVZZbXU2VUFEaE5pUHZjUGIxTG41Zm1DNTRsc3dVIiwi...

Decoded, a Credential looks like this:

1{
2 "challenge": {
3 "id": "IptU2AvkLwJDGuVYmu6UADhNiPvcPb1Ln5fmC54lswU",
4 "realm": "api.you.com",
5 "method": "tempo",
6 "intent": "charge",
7 "expires": "2026-08-03T14:24:11Z",
8 "request": "eyJhbW91bnQiOiIxMDAwMCIsImN1cnJlbmN5IjoiMHgyMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDBiOTUzN2QxMWM2MGU4YjUwIiwibWV0aG9kRGV0YWlscyI6eyJjaGFpbklkIjo0MjE3fSwicmVjaXBpZW50IjoiMHguLi4ifQ"
9 },
10 "payload": {
11 "type": "transaction",
12 "signature": "0x1b2c3d4e5f6a7b8c9d0e..."
13 },
14 "source": "did:pkh:eip155:4217:0x1234567890abcdef1234567890abcdef12345678"
15}
FieldContents
challengeThe Challenge being answered, with wire values preserved exactly.
payloadMethod-specific proof of payment.
sourceIdentity of the payer, as an address, DID, or account ID.

Each Credential is valid for exactly one request. Replays are rejected.


Making a Paid Request

The mppx SDK polyfills fetch, so your existing code works unchanged. The 402, the payment, and the retry all happen underneath.

$npm install mppx
1import { Mppx, tempo } from "mppx/client";
2import { privateKeyToAccount } from "viem/accounts";
3
4const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
5
6Mppx.create({
7 methods: [tempo({ account })],
8});
9
10const params = new URLSearchParams({
11 query: "best practices for scaling microservices in production",
12 count: "10",
13});
14
15// Mppx.create() polyfills global fetch, so this is a normal fetch call.
16const response = await fetch(`https://api.you.com/v1/search?${params}`);
17
18const data = await response.json();
19console.log(data.results.web.map((r) => r.url));

Official SDKs are also available for Python (pympp), Rust (mpp-rs), Go (mpp-go), and Ruby (mpp-rb). See the MPP SDK documentation.

Agent Wallets

If you are wiring up an autonomous agent rather than an application, several managed wallets handle funding and spend controls for you, including Tempo Wallet and the Privy Agent CLI. Both let a human set spend limits and revoke access without the agent holding a private key directly.


Receipts

On success we return a base64url-encoded Receipt in the Payment-Receipt header:

1{
2 "challengeId": "IptU2AvkLwJDGuVYmu6UADhNiPvcPb1Ln5fmC54lswU",
3 "method": "tempo",
4 "reference": "0xtx789abc...",
5 "settlement": {
6 "amount": "10000",
7 "currency": "0x20c000000000000000000000b9537d11c60e8b50"
8 },
9 "status": "success",
10 "timestamp": "2026-08-03T14:24:11Z"
11}

The reference field is the Tempo transaction hash. Log it if you need an audit trail. There is no invoice and no credit-history line item, because there is no account.

The field names above follow the MPP specification. Reading a Receipt requires settling a real payment, so parse defensively rather than asserting on this exact shape, and tell us if a live Receipt disagrees.


Limits and Safeguards

Expiry. Challenges expire at expires, 10 minutes after issue. Request a fresh Challenge rather than reusing an expired one.

Single use. One Credential satisfies one request.

Rate limits. Challenge issuance is capped at 10 per minute per caller. An 11th unpaid request inside the same minute returns 429 with a Retry-After header and a https://paymentauth.org/problems/rate-limited problem type instead of a fresh Challenge. Hold the Challenge you were issued rather than minting one per attempt. Keyless requests cannot inherit an organization’s custom QPS limit.

Finality. Stablecoin settlement is final, and there is no testnet to rehearse on.


Data Retention

Zero Data Retention is not available on MPP requests.

ZDR is an account-level configuration. It is added to an enterprise agreement and enforced against a specific organization’s API keys. An MPP request presents no API key and no account, so there is no agreement to attach ZDR to and no identity to enforce it against. Keyless requests fall outside ZDR and are handled under You.com’s general terms.

Two consequences to plan around:

  • There is no account-scoped deletion path. Deletion and data subject requests are handled per organization. An MPP request has no organization, so there is no established route to scope such a request to your traffic.
  • Routing end-user queries through this path is a decision you own. If you have a retention, residency, or compliance commitment to your own users, send those queries with an API key on an enterprise agreement that has ZDR enabled.

The payer identity in a Credential’s source field is a wallet address, DID, or account ID on the payment rail. It is not a You.com account and does not carry any data handling configuration with it.

If ZDR is a requirement, use API key authentication and ask your account team about adding ZDR to your agreement.


Errors

A 402 response body is application/problem+json:

1{
2 "type": "https://paymentauth.org/problems/payment-required",
3 "title": "Payment Required",
4 "status": 402,
5 "detail": "Payment is required.",
6 "challengeId": "N4YKVcT-d0n6XgCTNZQV9sruH-JdA1LhJh-2fyU1Z_A"
7}
StatusConditionWhat to do
402No Authorization headerExpected on the first call. Read the Challenge and retry.
402Credential present but not validA malformed, expired, or unverifiable Credential re-issues a fresh Challenge rather than returning a distinct status. Pay against the new challengeId.
401Right endpoint, wrong verbWeb Search offers payment on GET, Finance Research on POST. Switch the method rather than the endpoint.
401Invalid X-API-Key sentThe key path and the payment path are separate. Send a Credential and no key, or a valid key and no Credential.
429More than 10 unpaid Challenge requests in a minuteHonor Retry-After. Reuse the Challenge you already hold instead of minting a new one per attempt.
5xxSettlement or upstream failureRetry with backoff. See troubleshooting below.

Troubleshooting

I get a 402 loop. Your client is most likely re-encoding the request string instead of echoing it. Pass the Challenge parameters back exactly as they arrived.

I am getting 401 instead of 402. Check the verb before anything else. Web Search offers payment terms on GET /v1/search and Finance Research on POST /v1/finance_research. The inverse of either returns 401 with no hint that the method is the problem. The Contents and Research APIs return 401 on every method, since they do not accept machine payments at all.

My wallet has USDC but verification fails. Confirm the balance is on Tempo, chain ID 4217, in the token named by currency. USDC on Base or Solana cannot satisfy a Tempo Challenge. If your funds are on Base or Solana, use x402 instead.

Payment settled but the request returned a 5xx. A Challenge is valid for exactly one request, so a retry needs a fresh Challenge and settles a second payment. Keep the Receipt from the failed call and contact support with it rather than absorbing the double charge.

Can I use MPP and an API key on the same endpoint? Yes, on the same route, but not in the same request. Requests are routed by what they present.

Should I use MPP or x402? Pick by where your funds are. x402 covers Base and Solana, MPP covers Tempo, and both reach the same two endpoints. Price only separates them on Web Search, where x402 charges $0.005 against MPP’s $0.01. Finance Research costs the same on either. So if you are starting from scratch and most of your traffic is search, x402 is cheaper today. Reach for MPP when your agent is on Tempo, or when you want the integration that will pick up card and fiat rails as we enable them. Both are supported and neither is being deprecated.



Questions & Support

For MPP questions, reach us in Discord or contact [email protected].