HTTP 402 Payment Required - a practical guide for APIs
TL;DR: 402 is a compact contract between your API and a client: challenge with payment details, receive a verifiable receipt, return the resource. Used well, it keeps monetization inside HTTP without detours through dashboards or manual flows.
Why 402 now
APIs increasingly serve automated clients and agents. 402 makes “paid access” programmable, observable, and testable in the same loop as your normal requests.
Anatomy of a 402 response
status: 402detail: human-readable reasonpayment: a minimal JSON hint (protocol, amount, currency, reference, instructions)
HTTP/1.1 402 Payment Required
Content-Type: application/json; charset=utf-8
Cache-Control: no-store
{
  "detail": "Payment required to access this resource.",
  "payment": {
    "protocol": "x402",
    "amount": "0.10",
    "currency": "USDC",
    "reference": "demo-402-abc123",
    "instructions": "Pay using your client wallet and present a receipt in the follow-up request."
  }
}Design rules we follow
- Keep it small: only fields your client truly needs.
 - Reference everything: include a stable 
referenceyou can map to an entitlement. - Be explicit about expiry: short-lived challenges reduce replay risk.
 - Deterministic errors: return clear reasons for insufficient/expired/invalid receipts.
 - Log enough to audit: challenge ID, receipt hash, verification outcome.
 
Flow: request → 402 → pay → receipt → access
- Client requests a priced resource.
 - Server returns 402 with a payment hint.
 - Client pays, obtains a receipt/proof.
 - Client retries with the receipt in headers or body.
 - Server verifies and returns 
2xxor a precise error. 
Common pitfalls
- Leaking pricing logic into every handler. Centralize challenge creation.
 - Opaque errors. Machines need deterministic reasons.
 - No mapping from 
referenceto entitlement. Keep a ledger. 
FAQ
Does 402 work with existing API keys?
Yes. Keep your auth. 402 simply meters priced endpoints.
Where does the receipt live?
Usually in a header or JSON field on the retry. Verify server-side, then cache the grant.