A paid response creates an awkward caching question. If an agent buys the same weather forecast twice within a minute, should it pay twice? Probably not. But a public cache that returns the first buyer’s response to anyone else has leaked the product.
Across 789 endpoints priced from $0.001 to $0.50, cache policy can’t be guessed from price. The endpoint has to say what’s safe.
CDN caching and client caching aren’t equivalent
A CDN cache is shared. Unless configured otherwise, callers requesting the same resource can receive the same stored response.
That’s dangerous for x402. A cache hit could bypass payment verification and expose a response purchased by another caller. Most CDNs won’t cache a POST by default, but defaults change through route rules and proxy settings. Don’t rely on luck.
If data is cached at the edge, payment verification still has to run before the stored body is released. The final paid response shouldn’t enter a public cache keyed only by its URL.
A client cache has a smaller trust boundary. The buying agent stores its own response and reuses it for an allowed period. No second caller gets access, and no second USDC payment is needed during that period.
One warning: payment headers aren’t cache credentials. A settlement receipt belongs to the purchase that produced it. Don’t attach a cached PAYMENT-RESPONSE value to another request.
Headers agents can obey
For paid data that remains useful briefly, we use a private client TTL and block shared storage:
Cache-Control: private, max-age=300 CDN-Cache-Control: no-store
private permits storage inside the caller’s cache. max-age=300 makes the response fresh for five minutes. CDN-Cache-Control: no-store gives shared infrastructure an explicit instruction even if it applies separate CDN rules.
Paid surfaces can also return:
Vary: PAYMENT-SIGNATURE
That prevents a payment-bearing request from sharing a cache entry with a request carrying different payment data. It’s defense in depth. It doesn’t make public caching safe.
The initial 402 Payment Required response gets stricter treatment:
Cache-Control: no-store, max-age=0 CDN-Cache-Control: no-store
A stale challenge can contain an old price or payment destination. Agents should read the current challenge before signing.
Weather gets five minutes
weather-forecast costs $0.002 per call and accepts coordinates such as:
{
"latitude": 37.7749,
"longitude": -122.4194,
"units": "imperial",
"forecast_days": 3
}
Conditions don’t gain much from two calls made 40 seconds apart. An agent can build a local cache key from the endpoint URL and canonical JSON body, then keep the paid response for 300 seconds.
Fresh entry? Return it locally. Stale entry? Send a new request, handle the current x402 challenge, and replace the saved response after payment.
Many HTTP clients won’t automatically cache POST responses. Agent runtimes should parse Cache-Control and maintain their own response store. They also shouldn’t pay merely to revalidate an expired entry with If-None-Match, since the revalidation request may still cross the payment gate.
Token risk stays fresh
token-risk costs $0.10 per call. Its answer can change as liquidity moves and holder distribution shifts. A score used before a swap shouldn’t come from a five-minute-old response unless the application has explicitly accepted that delay.
So the response uses the same no-store policy as the 402 challenge:
Cache-Control: no-store, max-age=0 CDN-Cache-Control: no-store
You can retain the result as an audit record with its timestamp and transaction hash. Don’t return that record later as a current risk check. An audit record is evidence. A cache hit claims freshness.
Give the router a direct policy
An LLM router shouldn’t infer cacheability from endpoint names. Translate response headers into an explicit local rule:
{
"weather-forecast": {
"scope": "caller",
"ttl_seconds": 300
},
"token-risk": {
"scope": "none",
"ttl_seconds": 0
}
}
For weather-forecast, identical input inside the TTL returns the buyer’s saved response. For token-risk, ttl_seconds is zero, so the router pays for a current answer every time.