How to Charge Different Prices for the Same Endpoint with x402
A common question from developers building paid APIs with x402 is: how do I charge different prices for the same endpoint based on some criteria? With x402 SDK v2, you can do exactly that by using a custom function for the price (and optionally for the payment recipient) instead of a fixed value. The middleware calls your function on each request and uses the returned price when returning the402 Payment Required response and when verifying and settling the payment.
This article shows how to implement dynamic pricing in every supported stack: TypeScript (Next.js, Express, Hono, and by hand), Python (Flask, FastAPI, and by hand), and Go (Gin and by hand).
The core concept: the pricing function
What makes dynamic pricing possible in x402 is that price (and optionally payTo) can be a function of the request instead of a static value. The server middleware invokes your function on every request and uses the returned price when it sends the402 Payment Required response and when it verifies and settles the payment. The same idea applies across TypeScript and Python; Go uses a manual approach.
Function signature (TypeScript and Python)
In both TypeScript and Python, the contract is the same:- Input: A request context object that gives you access to the incoming HTTP request (headers, query params, path, method, and optionally body).
- Output: A price for that request—typically a string like
"$0.01"or a scheme-specific price object (e.g.AssetAmountin Python for custom tokens).
| Language | Price function signature | Context type / adapter |
|---|---|---|
| TypeScript | (context: HTTPRequestContext) => Price or Promise<Price> | context.adapter: getHeader(name), getQueryParam(name), getMethod(), getPath(), getUrl(), optional getBody(). |
| Python | Callable that takes HTTPRequestContext and returns a price (e.g. str or AssetAmount) | context.adapter: get_header(name), get_query_param(name), plus method/path/URL. Sync for Flask; sync or async for FastAPI. |
| Go | No built-in pricing function. You compute the price in your handler (e.g. from query or header), then build the route config or payment requirements for that request yourself before calling the x402 middleware or facilitator. | N/A — you read the request (e.g. c.Query("tier"), c.GetHeader("X-Tier")) and pass the result into your config. |
accepts[].price (and optionally accepts[].payTo) in your route config for Express, Hono, or Next.js. In Python you pass it as PaymentOption(..., price=get_tier_price, ...) in your RouteConfig. The middleware then calls your function when it needs to build the payment requirements for that request. Once you see that, the rest of this article is just framework-specific wiring.
Use case: tier-based pricing
We’ll use one scenario for all examples so you can compare approaches:- Endpoint:
GET /api/insight(or the framework’s equivalent). - Behavior: The client sends a tier via query parameter or header:
basic,plus, orpro. - Prices:
basic→ $0.01plus→ $0.05pro→ $0.10
basic.
The same pattern works for other criteria (e.g. authenticated user, plan, or custom header).
TypeScript
Next.js
UsewithX402 for API routes so payment is settled only after a successful response. The route config can use a function for price (and payTo if needed). The function receives a request context with adapter (headers, query, body, path, method).
Example: app/api/insight/route.ts (or under your api directory):
proxy.ts (or shared server setup) should export server, paywall, and evmAddress / svmAddress as in the Next.js x402 example.
Express
UsepaymentMiddleware with a route config where accepts[].price is a function. The middleware passes a context whose adapter exposes the request (e.g. getQueryParam, getHeader).
Hono
UsepaymentMiddleware from @x402/hono with a route config where accepts[].price is a function. The middleware passes a context whose adapter exposes the request (e.g. getQueryParam, getHeader). Attach the middleware with app.use(), then define your route; run the server with serve({ fetch: app.fetch, port }).
Custom (by hand)
The important part is thatRouteConfig.accepts[].price (and optionally payTo) can be a function (context: HTTPRequestContext) => Price | Promise<Price>. The HTTP resource server calls it when building the payment requirements for that request. “By hand” you wire the same x402HTTPResourceServer and dynamic route config into your own HTTP stack instead of using Express, Hono, or Next.js.
Without one of those frameworks, you can still use the same dynamic price support from the SDK:
- Create an
x402ResourceServerand register schemes (EVM, SVM, etc.). - Create an
x402HTTPResourceServerfrom@x402/core/serverwith a single route whoseaccepts[].priceis yourgetTierPricefunction (and optionallypayToas a function). - Use
paymentMiddlewareFromHTTPServer(httpServer)from@x402/express(or the Hono equivalent) to plug that HTTP server into any stack that can run the middleware, or drive it yourself: on each request, build anHTTPRequestContextwith an adapter that implementsgetHeader,getMethod,getPath,getUrl, and optionallygetQueryParam/getBody, then callhttpServer.processHTTPRequest(context)and handle the result (402, payment-verified, or no-payment-required). - If the result is payment-verified, run your handler, then call
httpServer.processSettlement(...)with the captured payload and requirements.
Python
Flask
Usepayment_middleware with a RouteConfig whose PaymentOption.price is a callable that receives HTTPRequestContext. The context has adapter (e.g. get_query_param, get_header). Use the sync server and sync callables for Flask.
FastAPI
UsePaymentMiddlewareASGI from x402.http.middleware.fastapi with a RouteConfig whose PaymentOption.price is a callable that receives HTTPRequestContext. The context has adapter (e.g. get_query_param, get_header). Use the async HTTP resource server; your price callable can be sync or async—the server will await it when building payment requirements.
Custom (by hand)
The important part is thatPaymentOption.price (and optionally pay_to) can be a callable that takes HTTPRequestContext and returns a price (e.g. a string like "$0.01" or an AssetAmount). The context’s adapter exposes the request (e.g. get_query_param, get_header). The HTTP resource server calls your callable when building the payment requirements for that request. “By hand” you wire the same x402HTTPResourceServer (or sync variant) and dynamic route config into your own stack instead of using Flask or FastAPI middleware.
Without that middleware, you can still use the same dynamic price support:
- Build an
x402ResourceServer(sync or async) and register schemes. - Build an
x402HTTPResourceServerorx402HTTPResourceServerSyncwith a single route whosePaymentOption.priceis yourget_tier_price(context)callable. - For each request, wrap the request in the framework’s adapter, build an
HTTPRequestContext, and callhttp_server.process_http_request(context)(or the async variant). - If the result is payment-verified, run your handler, then call
process_settlementwith the returned payload and requirements.
Go
Gin
The Go x402 HTTP layer currently expects static route configs (no function type for price in the public API). You can still implement tier-based pricing in two ways: Option A – Multiple route patterns (static configs)Register separate route patterns per tier, e.g.
GET /api/insight/basic, GET /api/insight/plus, GET /api/insight/pro, each with its own fixed price in RoutesConfig. The client then calls the path that matches their tier. This is “different prices” by using different routes, not one endpoint with a dynamic function.
Option B – One route, dynamic price by handUse a single route that does not go through the middleware’s static route table for payment. In the handler, read
tier from the request, compute the price, build payment requirements (and optionally call the facilitator’s verify/settle), and return 402 or proceed. That means you implement the 402 flow yourself for that route while still using the same facilitator and schemes elsewhere.
Example sketch for Option B with Gin: in a handler, get the tier from query or header, map it to a price string, build a RouteConfig-like struct for that request, then use the Go HTTP server’s low-level APIs (if exposed) to run verify/settle with that config. The Gin example uses static RoutesConfig; for true per-request price you’d call the resource server’s verify/settle with requirements you build from the current request’s tier.
Custom (by hand)
Fully by hand in Go you:- Parse the request (path, method, query, headers) and decide the price (e.g. from
tier). - Build the payment requirements (scheme, network, payTo, price, etc.) for that request.
- If there is no valid
PAYMENT-SIGNATURE(or equivalent), respond with 402 and thePAYMENT-REQUIREDbody/header. - If the client sends a payment, call the facilitator’s verify endpoint with the payload and your requirements; if valid, run your business logic, then call settle, and return the resource with the settlement response.
Summary
| Stack | How to get different prices for the same endpoint |
|---|---|
| TS (Next/Express/Hono) | Use a function for accepts[].price (and optionally payTo) in the route config. The middleware passes a request context; your function returns the price for that request. |
| Python (Flask/FastAPI) | Use a callable for PaymentOption.price (and optionally pay_to) that takes HTTPRequestContext. Sync for Flask (sync server), sync or async for FastAPI (async server). |
| Go (Gin) | Use multiple static routes (different paths per tier) or implement one route “by hand”: compute price from the request, build requirements, then verify/settle via the facilitator. |

