Skip to main content
402

Payment Required

Active
RFC 9110 §15.5.3Since 1999SaaS APIs, payment gateways, subscription platforms, paywalls, metered services

HTTP 402 Payment Required indicates that the request cannot be fulfilled until the client provides valid payment. Defined in RFC 9110 §15.5.3. Originally reserved for future digital cash schemes, it is now widely adopted by APIs to signal expired subscriptions, declined payments, or paywall-gated content.

Description

The 402 Payment Required status code was reserved by the HTTP specification for future use — specifically anticipated for digital cash and micropayment systems that were expected to proliferate in the late 1990s. Those systems never materialized in the form the spec authors imagined, so the code sat in limbo: officially reserved, practically orphaned.

Despite its 'reserved' status, 402 has been adopted in the real world by dozens of APIs and platforms. Stripe returns 402 when a charge fails. Shopify uses it for stores with billing issues. SaaS platforms use it when a subscription lapses, a trial expires, or a feature requires a higher pricing tier. The semantic meaning has crystallized through practice: 'you need to pay, or pay more, to access this resource.'

The W3C Payment Request API and the Web Monetization specification gave 402 renewed technical relevance. The idea of a browser-native payment flow — where a server returns 402 and the client automatically initiates a payment handshake — is closer to reality than it was in 1999. Some argue 402 is now the most semantically correct code for paywall situations, more precise than 403 (which implies permanent denial) or 401 (which implies authentication, not payment).

Philosophically, 402 occupies a unique position in HTTP: a code that was never formally standardized for use, yet became a de facto standard through adoption. It proves that protocol semantics are ultimately defined by what implementations agree on, not just what RFCs prescribe.

When returning 402, servers SHOULD include a response body explaining what payment is required, how to make it, and what resource or action will be unlocked. Unlike 401, there is no standard challenge mechanism (no WWW-Authenticate equivalent for payments), so the response body carries the entire burden of guiding the client toward resolution.

Examples

API request with expired subscription
http
GET /api/v1/reports/analytics HTTP/1.1
Host: api.example.com
Authorization: Bearer sk_live_abc123
Accept: application/json
402 response — subscription expired
http
HTTP/1.1 402 Payment Required
Content-Type: application/json

{"error": "payment_required", "message": "Your subscription expired on 2024-01-15. Please update your billing to continue accessing analytics.", "subscription_status": "expired", "expired_at": "2024-01-15T00:00:00Z", "upgrade_url": "https://example.com/billing", "required_plan": "pro"}
Charge attempt with declined card
http
POST /v1/charges HTTP/1.1
Host: api.stripe.com
Authorization: Bearer sk_live_xyz789
Content-Type: application/x-www-form-urlencoded

amount=2000&currency=usd&source=tok_chargeDeclined
402 response — card declined
http
HTTP/1.1 402 Payment Required
Content-Type: application/json

{"error": {"type": "card_error", "code": "card_declined", "decline_code": "insufficient_funds", "message": "Your card has insufficient funds.", "charge": "ch_1234567890", "param": "source"}}
Accessing feature behind paywall
http
GET /api/v2/datasets/premium-market-data HTTP/1.1
Host: data.example.com
Authorization: Bearer user_token_456
X-Subscription-Tier: free
402 response — feature requires upgrade
http
HTTP/1.1 402 Payment Required
Content-Type: application/json

{"error": "upgrade_required", "message": "Premium market data requires a Business plan or higher.", "current_plan": "free", "required_plan": "business", "feature": "premium-market-data", "pricing_url": "https://data.example.com/pricing", "monthly_cost": "$49.00"}

Edge Cases

  • 402 has no standard challenge header (unlike 401's WWW-Authenticate). The response body must carry ALL payment context: what's owed, how to pay, and what unlocks. There is no machine-negotiable payment flow in core HTTP yet.
  • Distinguishing 402 from 403: use 402 when payment CAN resolve the block (subscription renewal, card update, plan upgrade). Use 403 when the denial is permanent or policy-based regardless of payment (banned account, geographic restriction, legal compliance).
  • For metered/usage-based APIs: 402 is appropriate when the client has exhausted paid credits or quota. If it's a rate limit on a paid plan (temporary throttle), 429 is more appropriate. The distinction is whether PAYMENT resolves it vs. TIME resolves it.
  • Some clients and proxies don't recognize 402 and may treat it as a generic 4xx. Ensure your error response body is self-explanatory even if the client doesn't have specific 402 handling logic.
  • Trial expirations are a gray area: if the trial requires a credit card to continue (converting to paid), 402 is correct. If the trial simply ended and the user needs to sign up (create a new relationship), 403 with instructions may be clearer.
  • Web Monetization spec proposes using 402 with a Link header pointing to a payment pointer. This is experimental but represents the closest thing to a standard machine-readable 402 flow: the server says 'pay here' and the browser/extension can act on it.
  • Returning 402 for API key issues (invalid key, revoked key) is WRONG — that's 401 (authentication) or 403 (authorization). 402 specifically means the identity is known and authenticated, but their PAYMENT status prevents access.

When You'll See This

  • Credit card on file declined during a recurring subscription charge
  • Free trial expired and user must provide payment to continue
  • API quota exceeded on a metered billing plan — more credits must be purchased
  • Accessing a premium feature that requires a higher subscription tier
  • Payment processor returns a decline and the upstream API surfaces it as 402
  • Content behind a paywall requested without active subscription
  • Shopify store suspended due to unpaid invoices
  • Cloud service resource provisioning blocked until outstanding balance is cleared

Implementation References

LanguageConstant
Gohttp.StatusPaymentRequired
Rusthttp::StatusCode::PAYMENT_REQUIRED
Pythonhttp.HTTPStatus.PAYMENT_REQUIRED
Node.jshttp.STATUS_CODES[402]
.NETHttpStatusCode.PaymentRequired
JavaHttpURLConnection.HTTP_PAYMENT_REQUIRED
PHPResponse::HTTP_PAYMENT_REQUIRED (Symfony)
Ruby:payment_required (Rack)

History

Introduced in HTTP/1.1 (RFC 2068, January 1997) as 'reserved for future use.' Carried forward unchanged through RFC 2616 (1999) and RFC 7231 (2014). RFC 9110 (June 2022) retained the reservation while acknowledging its ambiguous practical status. Despite never receiving formal standardization of its semantics, widespread API adoption (Stripe, Shopify, Cloudflare, numerous SaaS platforms) has established a de facto meaning since approximately 2011.

Related Status Codes

Related Headers

FAQ

Is HTTP 402 safe to use in production even though it's 'reserved for future use'?

Yes. Despite the RFC's 'reserved' language, 402 is widely used in production by major platforms including Stripe, Shopify, Cloudflare, and hundreds of SaaS APIs. All HTTP client libraries recognize it, all browsers handle it as a standard 4xx response, and the IANA registry lists it as a valid status code. The reservation language means no standard challenge/response mechanism has been formalized (unlike 401's WWW-Authenticate), but the code itself is stable and universally supported. Use it when payment is genuinely the blocker.

When should I use 402 vs 403 for subscription or billing issues?

Use 402 when the SPECIFIC resolution is payment: renew subscription, update expired card, purchase additional credits, or upgrade to a higher tier. The user is authenticated and identified — their payment status is the sole barrier. Use 403 when the denial isn't resolvable through payment: account banned for ToS violation, feature restricted by admin policy, geographic compliance block. The test: if the user paying money (right now, without changing anything else) would grant access, it's 402. If not, it's 403.

What should a 402 response body contain for best client experience?

Include: (1) what is blocked and why (expired subscription, declined card, insufficient tier), (2) current status (plan name, expiration date, balance), (3) what's needed to resolve (plan to upgrade to, amount owed, card update), (4) actionable URL (billing page, pricing page, payment portal), and (5) machine-readable fields (required_plan, upgrade_url, expired_at) so clients can programmatically guide users. The goal is making payment resolution achievable without additional API calls or support tickets.