Skip to main content
MPP (Machine Payment Protocol) is an open standard for machine-to-machine HTTP micropayments built on the HTTP 402 challenge-response flow. Dune’s MPP integration lets agents pay per-request through a payment channel - no API keys required.

How It Works

  1. Your client sends a request to a Dune API endpoint.
  2. Dune responds with HTTP 402 and a WWW-Authenticate: Payment challenge describing the cost.
  3. The mppx client library automatically opens a Tempo payment channel on the first 402, depositing funds into an on-chain escrow.
  4. Subsequent requests are authenticated with signed channel vouchers deducted from the deposit.
  5. When you are done, you close the channel and any unspent deposit is returned to your wallet.

Available Endpoints

EndpointDescription
POST /v1/sql/executeExecute a SQL query
GET /v1/execution/:execution_id/resultsFetch JSON results for an execution
GET /v1/execution/:execution_id/csvDownload CSV results for an execution

Integration Guide

This walkthrough uses the mppx client library and viem for EVM account management. The mppx session API wraps the standard fetch and handles the 402 negotiation, channel management, spend tracking, and payment signing transparently.

Step 1 - Install dependencies

npm install mppx viem

Step 2 - Set up your wallet and initialize a session

Create an account from a private key and initialize a Tempo session. The maxDeposit parameter sets the maximum amount (in USD) to escrow when a payment channel is opened.
import { tempo } from "mppx/client";
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount(
  process.env.PRIVATE_KEY as `0x${string}`
);
const session = tempo.session({ account, maxDeposit: "10" });

Step 3 - Execute a query

Use session.fetch exactly like the standard Fetch API. On the first call, the session intercepts the 402 response, opens a Tempo payment channel, and retries the request with a valid payment credential - all transparently.
const execRes = await session.fetch("https://api.dune.com/api/v1/sql/execute", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ sql: "SELECT * FROM ethereum.transactions LIMIT 10" }),
});
const { execution_id } = (await execRes.json()) as { execution_id: string };

Step 4 - Poll until the query completes

Dune executes queries asynchronously. Poll the status endpoint until the state is QUERY_STATE_COMPLETED.
let state = "";
do {
  const r = await session.fetch(
    `https://api.dune.com/api/v1/execution/${execution_id}/status`
  );
  state = ((await r.json()) as { state: string }).state;
  if (state === "QUERY_STATE_FAILED") throw new Error("Query failed");
  if (state !== "QUERY_STATE_COMPLETED")
    await new Promise((r) => setTimeout(r, 2_000));
} while (state !== "QUERY_STATE_COMPLETED");

Step 5 - Fetch results

Once the query completes, retrieve the results. Each request is automatically paid through the open channel.
const resultsRes = await session.fetch(
  `https://api.dune.com/api/v1/execution/${execution_id}/results`
);
console.log(await resultsRes.json());

Step 6 - Close the payment channel

When you are finished, close the channel to settle on-chain and reclaim any unspent deposit. The session tracks spending automatically and only pays for what you used.
await session.close();

Learn More