{"slug": "a-search-api-went-x402-native-today-here-is-its-402-byte-for-byte", "title": "A search API went x402-native today. Here is its 402, byte for byte.", "summary": "Brave announced that its Search API now accepts x402 payments with no account or API key, charging $0.005 per request in USDC on Base, and an operator of a competing x402 endpoint tested the service by issuing an unpaid request and parsing the returned 402 challenge. The decoded challenge, a 960-byte JSON document delivered base64-encoded in a 1,280-byte payment-required header with an empty body, passed a full schema parse against @x402/core 2.28.0 with zero issues, as did the operator's own challenge. The operator noted that the two independently written implementations conform to the same v2 schema, while Brave's challenge is far more compact than their own 2,610-byte document, which is shipped byte-identically in both header and body.", "body_md": "This content reflects minia2a as of its publication date and is kept for the record. Current model: x402 pay-per-call in **USDC on Base only**, **5 free trial calls per signed wallet**, no credits and no top-up rail.\n\nNovember 12, 2026 · measured at 19:34Z\n\nThis afternoon Brave announced that the Brave Search API accepts x402 payments: no account, no API key, USDC on Base, $0.005 per request. That is the first major independent search provider to sell per call on the protocol we run on, so we did the only thing worth doing — we called it and read the answer.\n\nWhat follows is not a summary of their blog post. It is one unpaid request, the response bytes it returned, and those bytes run through the same published schema gate we run our own endpoints through.\n\n```\ncurl -D - \"https://search.agent.s.brave.app/res/v1/llm/context?q=tallest+mountains\"\n```\n\nThe response header said `HTTP/2 402`. The body was **empty** — `content-length: 0`. Everything was in one header:\n\n```\npayment-required: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQYXltZW50IHJlcXVpcmVkIiwicmVzb3VyY2UiOns…\n```\n\nBase64, 1,280 bytes encoded. Decoded, 960 bytes of JSON:\n\n```\n{\n  \"x402Version\": 2,\n  \"error\": \"Payment required\",\n  \"resource\": {\n    \"url\": \"https://search.agent.s.brave.app/res/v1/llm/context?q=tallest+mountains\",\n    \"description\": \"Web search that returns page text ready for an LLM prompt. …\",\n    \"mimeType\": \"application/json\",\n    \"serviceName\": \"Brave Search\",\n    \"tags\": [\"search\", \"web\", \"news\", \"images\", \"llm\"],\n    \"iconUrl\": \"https://search.agent.s.brave.app/icon.png\"\n  },\n  \"accepts\": [{\n    \"scheme\": \"exact\",\n    \"network\": \"eip155:8453\",\n    \"amount\": \"5000\",\n    \"asset\": \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n    \"payTo\": \"0xFFa76529d7e733C7671190b374819d8eDA2b9D04\",\n    \"maxTimeoutSeconds\": 300,\n    \"extra\": {\n      \"assetTransferMethod\": \"eip3009\",\n      \"name\": \"USD Coin\",\n      \"version\": \"2\"\n    }\n  }],\n  \"extensions\": {\n    \"mppx\": { \"info\": { \"method\": \"GET\" }, \"schema\": { \"type\": \"object\" } }\n  }\n}\n```\n\nEverything checks out by eye: `eip155:8453` is Base, the asset is USDC, `5000` in 6-decimals is the advertised $0.005, and `maxTimeoutSeconds` is a number, not the string `\"300\"` that a surprising number of hand-rolled challenges ship.\n\nBy eye is not a verdict. x402 clients do not check \"is this object reasonable\" — the good ones run the document through `parsePaymentRequired`, which is a full schema parse. A challenge that fails it does not produce an error on the client side. It leaves `paymentRequired` unset, and the 402 gets forwarded or swallowed as an ordinary result. No retry, no payment, and the response looks correct to every human who inspects it.\n\nSo we ran Brave's document and one of ours through the published schema, `@x402/core` 2.28.0:\n\n``` bash\n$ node tools/x402-strong-gate-check.js < two-challenges.json\nschema: @x402/core 2.28.0\n{\"name\":\"brave\",\"ok\":true,\"issues\":[]}\n{\"name\":\"minia2a\",\"ok\":true,\"issues\":[]}\n```\n\nBoth pass, zero issues. Two implementations written by different teams against the same v2 schema, and the schema holds them both. That is the useful signal from today: the protocol is not just being adopted, it is being adopted *conformantly*.\n\nPut the two challenges side by side and one field tells a story about header budgets.\n\n|  | Brave Search | minia2a | \n|---|---|---|\n| x402Version | 2 | 2 | \n| network / asset | eip155:8453 / USDC | eip155:8453 / USDC | \n| amount (sample) | 5000 (~$0.005) | 50000 (~$0.05) | \n| maxTimeoutSeconds | 300 | 120 | \n| extra | assetTransferMethod, name, version | name, version | \n| extensions | mppx | bazaar + nextSteps in the body | \n| **decoded challenge** | **960 B** | **2,610 B** | \n| **header, base64** | **1,280 B** | **3,480 B** | \n| **message body** | **empty** | **equal to the decoded header** | \n\nWe ship the same document twice: in the `PAYMENT-REQUIRED` header and as the response body, byte-identical, and we have a guard that fails if those two ever drift. Brave ships it once, in the header, with `content-length: 0`.\n\nBoth are legal. But a client written to read only the body gets *nothing* from Brave, and a client written to read only the header works against both. If you are writing an x402 client, that is the whole lesson: the header is the carrier. The body is a convenience some sellers provide.\n\nOur challenge is 2.7× Brave's, because we attach a discovery schema and human-readable next steps to the same header. That is a product decision — a buyer who has never seen our catalog gets told how to use a trial from the 402 alone. It is not free: 3,480 bytes of base64 on every refusal.\n\nWe have already been bitten by this. A reverse proxy with a default header buffer truncates a challenge this size, and a truncated `PAYMENT-REQUIRED` is indistinguishable from a malformed one. Our proxy config now sizes its buffers above our worst challenge. If you add fields to your 402, measure the encoded size, not the JSON size — base64 costs 4/3.\n\n`extra` is not a break\nBrave sets `extra.assetTransferMethod: \"eip3009\"`. We do not. In the published client, the reader is:\n\n``` js\nconst atm = typeof requirements.extra?.assetTransferMethod === \"string\"\n  ? requirements.extra.assetTransferMethod\n  : scheme.defaultAssetTransferMethod;\n```\n\nAbsent means \"use the scheme default\", which for `exact` on EVM is EIP-3009. So our omission is safe today. It is also the kind of thing that becomes unsafe the day a default changes under you, which is the argument for being explicit when you control both sides of the wire.\n\nA vendor with a real search index decided that per-request USDC is a better front door than an API key and a billing account. Their 402 is the price — that is their own wording, and it is the right rule: whatever the marketing page says, the challenge is what the buyer's client will actually act on.\n\nThe facilitator role also moved today. Brave runs its own, rather than routing settlement through someone else's. That was a single-vendor role not long ago; now it is a thing a search company does on a Tuesday.\n\nFor us the takeaway is narrower and more useful: the conformance bar we built for ourselves — every one of our live 402s through the published schema, with the schema version printed next to the verdict — is the same bar a very large new entrant clears. That is what a spec looks like when it is working.\n\nMeasured 2026-11-12 19:34Z. One endpoint, one unpaid request, no payment sent. The challenge document is Brave's; the schema run is ours. Commands and the decoded documents are reproducible from what is printed above.\n\n*Originally published at [minia2a.uk](https://minia2a.uk/blog/brave-search-402-shape-november-2026).*", "url": "https://wpnews.pro/news/a-search-api-went-x402-native-today-here-is-its-402-byte-for-byte", "canonical_source": "https://dev.to/minia2a/a-search-api-went-x402-native-today-here-is-its-402-byte-for-byte-2lpj", "published_at": "2026-11-12 19:39:29+00:00", "updated_at": "2026-11-12 19:53:25.818965+00:00", "lang": "en", "topics": ["ai-agents", "agent-protocols", "ai-search", "ai-tools"], "entities": ["Brave", "Brave Search API", "x402", "USDC", "Base", "@x402/core", "minia2a"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/a-search-api-went-x402-native-today-here-is-its-402-byte-for-byte", "markdown": "https://wpnews.pro/news/a-search-api-went-x402-native-today-here-is-its-402-byte-for-byte.md", "text": "https://wpnews.pro/news/a-search-api-went-x402-native-today-here-is-its-402-byte-for-byte.txt", "jsonld": "https://wpnews.pro/news/a-search-api-went-x402-native-today-here-is-its-402-byte-for-byte.jsonld"}}