# Quicknode RPC via MPP (Machine Payments Protocol) > Pay-per-request blockchain RPC access using the Machine Payments Protocol (MPP). No signup, no API keys — just a wallet. Two payment methods: PathUSD on Tempo or USDC on Solana. ## How it works Quicknode RPC via MPP lets you access blockchain data through JSON-RPC and REST calls, paid for with stablecoin micropayments. MPP is an open protocol (IETF spec at https://paymentauth.org) for machine-to-machine payments. ### Payment Methods Two payment methods are supported. The server accepts either — choose whichever suits your stack: 1. **Tempo (PathUSD)** — Pay with PathUSD stablecoin on the Tempo blockchain. Supports both charge and session intents. 2. **Solana (USDC)** — Pay with USDC on Solana. Supports charge intent. Uses SPL token transfers verified on-chain via reference keys. ### Payment Intents 1. **Charge** ($0.001 per request) — One-time payment per request. The client signs a token transfer, the server settles it on-chain, and proxies the RPC request. Best for simple integrations and low-volume usage. 2. **Session** ($0.00001 per request) — Open a payment channel with an on-chain deposit, then send off-chain cumulative vouchers on each request. Ultra-low latency for high-volume usage ($10/million requests). ### Charge Flow 1. **Hit any endpoint** — `POST https://mpp.quicknode.com/:network` with a JSON-RPC or REST request and no `Authorization` header. 2. **Receive 402 challenge** — Response includes `WWW-Authenticate: Payment` header with a Challenge containing `method="tempo"` or `method="solana"`, `intent="charge"`, the amount, currency, and recipient. 3. **Pay** — Sign a token transfer for the specified amount and include the Credential in the `Authorization: Payment` header on retry. 4. **Access** — Server verifies the payment, settles the transaction on-chain, and proxies the request to Quicknode. Response includes a `Payment-Receipt` header with the transaction hash. ### Session Flow (pay-as-you-go) 1. **Open a session** — `POST https://mpp.quicknode.com/session/:network` with no credential. Receive 402 with session Challenge containing `intent="session"`, amount per request, and deposit requirements. 2. **Deposit** — Open a payment channel by depositing tokens into an on-chain escrow contract (~500ms first time). A unique `channelId` identifies your channel. 3. **Send vouchers** — Each subsequent request includes a cumulative EIP-712 voucher in `Authorization: Payment` (e.g., "I have now consumed up to X total"). No on-chain transaction per request — server verifies with a single `ecrecover`. 4. **Top up** — If the channel runs low, deposit additional tokens without closing. Session continues uninterrupted. 5. **Close** — Close the payment channel when done. Server settles the final balance on-chain. Unused deposit is refunded. ## Protocol Details MPP uses standard HTTP headers following the IETF Payment Authentication specification: - **Challenge:** `WWW-Authenticate: Payment id="...", realm="mpp.quicknode.com", method="tempo", intent="charge", request=""` (server -> client, on 402) - **Credential:** `Authorization: Payment ` (client -> server) - **Receipt:** `Payment-Receipt: ` (server -> client, on success) This is distinct from x402 which uses `PAYMENT-REQUIRED` and `PAYMENT-SIGNATURE` headers. Base URL: `https://mpp.quicknode.com` ### Tempo Chain **Tempo Chain:** Mainnet (chain ID 4217). PathUSD token at `0x20c0000000000000000000000000000000000000` (6 decimals, TIP-20 enshrined token — precompile-based, not a smart contract). ### Solana **Solana Network:** Mainnet. USDC token mint at `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` (6 decimals, SPL token). ## Bootstrapping — Tempo (PathUSD) 1. **Generate a wallet** — Use `generatePrivateKey()` and `privateKeyToAccount()` from `viem/accounts` to create a fresh keypair. 2. **Fund with PathUSD** — Acquire PathUSD on Tempo Mainnet (chain ID 4217) via a supported bridge or exchange. 3. **Install mppx** — `npm install mppx viem` 4. **Create client** — Call `Mppx.create()` with a `tempo({ account })` method. This polyfills `fetch` to handle 402 challenges automatically. 5. **Make RPC calls** — Use `fetch()` normally. Payment happens transparently when the server returns 402. ```typescript import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts' // No existing key needed — generate one on the fly const privateKey = generatePrivateKey() const account = privateKeyToAccount(privateKey) // Store privateKey securely — you will need it for future sessions ``` ## Bootstrapping — Solana (USDC) 1. **Generate a wallet** — Use `Keypair.generate()` from `@solana/web3.js` to create a fresh keypair. 2. **Fund with USDC** — Acquire USDC on Solana Mainnet via a supported exchange or bridge. 3. **Install solana-mpp** — `npm install solana-mpp mppx @solana/web3.js @solana/spl-token` 4. **Create client** — Call `Mppx.create()` with a `solana({ wallet })` method. 5. **Make RPC calls** — Use `fetch()` normally. Payment happens transparently when the server returns 402. ```typescript import { Keypair } from '@solana/web3.js' // No existing key needed — generate one on the fly const keypair = Keypair.generate() console.log('Public key:', keypair.publicKey.toBase58()) // Store keypair.secretKey securely — you will need it for future payments ``` ## Client Setup — Tempo Install: `npm install mppx viem` ### Polyfill mode (recommended — simplest) ```typescript import { Mppx, tempo } from 'mppx/client' import { privateKeyToAccount } from 'viem/accounts' const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY') // Polyfills globalThis.fetch — all subsequent fetch() calls handle 402 automatically Mppx.create({ methods: [tempo({ account })], }) // Use fetch normally — payment happens transparently on 402 const response = await fetch('https://mpp.quicknode.com/base-sepolia', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] }), }) const { result } = await response.json() console.log('Block number:', BigInt(result)) ``` ### Without polyfill ```typescript import { Mppx, tempo } from 'mppx/client' import { privateKeyToAccount } from 'viem/accounts' const account = privateKeyToAccount('0xYOUR_PRIVATE_KEY') const mppx = Mppx.create({ polyfill: false, methods: [tempo({ account })], }) // Use mppx.fetch instead of global fetch const response = await mppx.fetch('https://mpp.quicknode.com/base-sepolia', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] }), }) ``` ## Client Setup — Solana Install: `npm install solana-mpp mppx @solana/web3.js @solana/spl-token` ```typescript import { Mppx, solana } from 'solana-mpp/client' import { Keypair } from '@solana/web3.js' const keypair = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(process.env.SOLANA_KEY!))) Mppx.create({ methods: [solana({ wallet: keypair })], }) // Use fetch normally — payment happens transparently on 402 const response = await fetch('https://mpp.quicknode.com/solana-mainnet', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'getSlot', params: [] }), }) const { result } = await response.json() console.log('Slot:', result) ``` ## Charge-Only Client (Tempo or Solana) The charge endpoint (`/:network`) returns a 402 challenge listing **both** Tempo and Solana payment methods. Your client only needs to support one — pick whichever suits your stack. The `Authorization: Payment` credential tells the server which method you chose. - **Tempo charge** — Use `mppx/client` with `tempo({ account })`. Requires a viem account (EVM private key). - **Solana charge** — Use `solana-mpp/client` with `solana({ wallet })`. Requires a Solana Keypair with funded USDC. Both handle the 402 → credential → retry flow automatically when using polyfill mode. No session or payment channel setup required. ### Manual payment handling ```typescript import { Mppx, tempo } from 'mppx/client' import { privateKeyToAccount } from 'viem/accounts' const mppx = Mppx.create({ polyfill: false, methods: [tempo()], // no account here — passed per-request below }) // Step 1: Make request, get 402 challenge const response = await fetch('https://mpp.quicknode.com/base-sepolia', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] }), }) if (response.status === 402) { // Step 2: Create credential from challenge const credential = await mppx.createCredential(response, { account: privateKeyToAccount('0xYOUR_PRIVATE_KEY'), }) // Step 3: Retry with payment credential const paidResponse = await fetch('https://mpp.quicknode.com/base-sepolia', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: credential, }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] }), }) } ``` ### Payment receipts On success, the server returns a `Payment-Receipt` header: ```typescript import { Receipt } from 'mppx' const response = await fetch('https://mpp.quicknode.com/base-sepolia', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_blockNumber', params: [] }), }) const receipt = Receipt.fromResponse(response) console.log(receipt.status) // "success" console.log(receipt.reference) // "0xtx789abc..." (charge: tx hash, session: channelId) ``` ## Quick testing with the mppx CLI The `mppx` CLI makes paid HTTP requests with automatic payment handling: ```bash # Install globally npm install -g mppx # Create an account (generates a wallet) mppx account create # Make a paid JSON-RPC request mppx -X POST -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}' \ https://mpp.quicknode.com/base-sepolia ``` Environment variables: `MPPX_PRIVATE_KEY` (use a key directly), `MPPX_ACCOUNT` (default account name). ## Endpoints - `POST /:network/*` — JSON-RPC/REST proxy via charge intent (MPP payment required) - `POST /session/:network/*` — JSON-RPC/REST proxy via session intent (MPP session required) - `GET /llms.txt` — This document ### Charge endpoint: `https://mpp.quicknode.com/:network` Replace `:network` with any supported network slug (e.g., `base-sepolia`, `ethereum-mainnet`, `solana-mainnet`). Same network slugs as the x402 endpoint. Send a JSON-RPC or REST request: ``` POST /base-sepolia HTTP/1.1 Content-Type: application/json {"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]} ``` Without payment: returns 402 with `WWW-Authenticate: Payment` challenge. With valid `Authorization: Payment` credential: proxies to Quicknode and returns the response with `Payment-Receipt` header. ### Session endpoint: `https://mpp.quicknode.com/session/:network` Same format as charge but uses session vouchers for lower per-request cost. Session state (payment channel) persists across requests. ## Pricing | Intent | Cost per request | Token amount (6 decimals) | Best for | |---------|-----------------|--------------------------|----------| | Charge | $0.001 | 1,000 atomic units | Simple integrations, low volume | | Session | $0.00001 | 10 atomic units | High volume, agents, metered usage | ## Rate limits - `/:network`: 1,000 requests per 10 seconds per IP:network pair - `/session/:network`: 1,000 requests per 10 seconds per IP:session:network pair ## Error responses Errors return JSON with an `error` code and optional `message`: `{ "error": "", "message"?: "" }`. | Status | Error code | Description | |--------|-----------|-------------| | 402 | (MPP challenge) | Payment required — `WWW-Authenticate: Payment` header contains the Challenge | | 404 | `unsupported_network` | Network slug not recognized | | 429 | `rate_limit_exceeded` | Too many requests — back off and retry | | 503 | `mpp_not_configured` | MPP is not configured for this environment | ## Supported Networks Same networks as x402 — 140+ blockchain networks including all testnets. Discover all supported network slugs dynamically via `GET https://x402.quicknode.com/networks`. Common slugs: `ethereum-mainnet`, `base-sepolia`, `base-mainnet`, `polygon-mainnet`, `solana-mainnet`, `arbitrum-mainnet`, etc. ## npm packages - `mppx` — Official TypeScript SDK for the Machine Payments Protocol. Client (`mppx/client`), server (`mppx/server`), and framework middleware (`mppx/hono`, `mppx/nextjs`, `mppx/express`, `mppx/elysia`). Includes CLI. - `solana-mpp` — Solana payment method for MPP. Client (`solana-mpp/client`) and server (`solana-mpp/server`). - `viem` — Ethereum/Tempo wallet client, signing, and chain utilities (peer dependency of mppx) - `@solana/web3.js` — Solana web3 library (peer dependency of solana-mpp) ## Related - `https://x402.quicknode.com` — x402 payment protocol endpoint (SIWX auth, USDC payments on Base/Polygon/Solana) - [MPP Specification (IETF)](https://paymentauth.org) — Machine Payments Protocol spec - [MPP Documentation](https://mpp.dev) — Full protocol docs, guides, and SDK reference - [mppx npm package](https://www.npmjs.com/package/mppx) — TypeScript SDK for MPP - [solana-mpp npm package](https://www.npmjs.com/package/solana-mpp) — Solana payment method for MPP - [Solana MPP](https://mppsolana.com) — Solana MPP announcement and docs - [Tempo Documentation](https://docs.tempo.xyz) — Tempo blockchain docs