x402 Dynamic Pricing

February 19, 2026 ยท View on GitHub

Demand-based surge pricing for the x402 payment protocol. Built on x402 V2's dynamic pricing callback. Instead of a static amount in payment requirements, V2 lets the server provide a function evaluated per-request. This project plugs a real-time demand tracker into that callback.

How it works

A sliding window (60 seconds, ring buffer of 1-second buckets) counts incoming requests. The count maps to a 5-tier piecewise pricing curve with linear interpolation between tier boundaries. An EMA smoother prevents price jitter from short bursts.

TierDemand thresholdMultiplierPrice at $0.001 base
Base01.0x$0.001000
Normal501.5x$0.001500
Elevated2002.5x$0.002500
High1,0005.0x$0.005000
Surge5,00010.0x$0.010000

Between tiers, the multiplier is linearly interpolated. At 125 requests (midpoint of normal-elevated), the multiplier is 2.0x, giving $0.002000. All thresholds, multipliers, and the base price are configurable.

Quick start

Requires Node >= 18.

Server

pnpm install
cp .env.example .env
# Edit .env: set EVM_ADDRESS to your wallet
pnpm start

Simulator

cd simulator
pnpm install
pnpm dev

Opens a dashboard at localhost:5173 with demand pattern controls (organic, spike, flood, decay), live time-series charts, pricing curve visualization, and adjustable tier thresholds.

Super Bowl Simulation

The simulator runs its own PricingEngine instance with synthetic demand, it doesn't connect to the live server. Use GET /pricing/status for production monitoring.

The x402 integration point

This is the key mechanism. Instead of a static price string, the x402 middleware receives a function:

'GET /api/data': {
  accepts: {
    scheme: 'exact',
    network: NETWORK,
    payTo: EVM_ADDRESS,
    price: () => pricingEngine.getFormattedPrice(),
  },
},

Every 402 response evaluates getFormattedPrice() at that instant. The client sees maxAmountRequired as the ceiling but pays the current dynamic price.

Demand is recorded before the payment check, both initial requests (that get 402'd) and retries with payment count as demand signals.

API

EndpointAuthResponse
GET /api/datax402 (402 -> pay -> 200)Paywalled resource. Price reflects current demand.
GET /pricing/statusNoneFull snapshot: demand, raw/smoothed price, tier, config.
GET /healthNone{ ok: true, uptime }

Pricing engine API

import { PricingEngine } from './src/pricing-engine.js';

const engine = new PricingEngine(config);

engine.recordRequest(count);     // Feed demand signal
engine.getDemand();              // Requests currently in the sliding window
engine.getCurrentPrice();        // EMA-smoothed price,  main integration API
engine.getRawPrice();            // Unsmoothed instantaneous price
engine.getFormattedPrice();      // "\$0.001234"
engine.getTierInfo();            // { level, name, threshold, multiplier, demand }
engine.getStatus();              // Full snapshot (all of the above + config)
engine.reset();                  // Clear demand history and EMA state

Constructor accepts an optional config object. Any omitted fields fall back to defaults in src/config.js.

Configuration

Pricing parameters (src/config.js):

ParameterDefaultDescription
windowSize60Sliding window duration in seconds
bucketSize1Seconds per ring buffer bucket
basePrice0.001Base price in dollars
smoothingAlpha0.3EMA factor per second (0 = frozen, 1 = no smoothing)
tiersSee table aboveArray of { threshold, multiplier } objects

Environment variables (.env):

VariableRequiredDefault
EVM_ADDRESSYesโ€”
FACILITATOR_URLNohttps://facilitator.x402.org
PORTNo4021
NETWORKNoeip155:84532 (Base Sepolia)

Tests

pnpm test

26 tests across 5 suite for demand tracking, tier interpolation, EMA smoothing, output formats, and edge cases. Uses Node's built-in node:test, no test framework dependency.

Current Limitations

  • Single-node only: The sliding window lives in process memory. Multiple server instances behind a load balancer track demand independently. Distributed deployment needs a shared counter (Redis or similar).

  • No "gaming" protection: An actor who knows the tier boundaries can time requests to stay just below a threshold. Highly recommended to pair with rate limiting in production.

  • EMA lag is by design: After a demand spike, the smoothed price takes several seconds to converge to the raw price. After demand drops, it takes several seconds to decay. This prevents jitter but means the paid price lags the "true" instantaneous price.