What integration means
Integrating payments into DSPy means two related things: giving a module the ability to pay for what it calls (a paid API, a data source, another agent), and, when you want it, charging other callers for what your DSPy program serves. The first is the common case; the second is the earn side, turning a DSPy program into a paid service.
This page is the integration reference: the pieces, how they fit, and the one DSPy-specific point about how payment relates to optimization. For the same overview in sibling frameworks, see crewai-payment-integration and agno-payment-integration. The payment API product page is the broader reference. Here the focus is the shape of a DSPy integration, expressed as a tool a module can call.
The Node proxy reality
Start with the fact that shapes everything: DSPy is Python, and the payment packages, @blockchain0x/x402 and @blockchain0x/node, ship for Node. There is no Python payment package, so you do not call Blockchain0x from inside the module directly.
Instead, the payment work runs in a small Node process and the module talks to it over localhost. On the pay side this is a proxy that holds the wallet; on the earn side it is a gateway that gates an endpoint. Both are small, about thirty lines, and both keep every payment identifier on the verified Node surface. It is the same pattern used across the Python-framework integrations, and for DSPy you wrap the call to that proxy in a dspy.Tool.
A dspy.Tool in a ReAct module
DSPy modules that act, like dspy.ReAct, take tools, so the integration is a dspy.Tool whose function calls the proxy.
import requests
import dspy
def pay_and_fetch(url: str) -> str:
"""Fetch a URL, paying in USDC if it requires payment. Returns the body."""
res = requests.post("http://127.0.0.1:8787", json={"url": url}, timeout=30)
return res.text if res.status_code == 200 else f"Failed: {res.status_code}"
pay_tool = dspy.Tool(pay_and_fetch)
agent = dspy.ReAct("question -> answer", tools=[pay_tool])Start the proxy, and the ReAct module can pay: when its reasoning decides a step needs a paid resource, it calls pay_and_fetch, the proxy settles the 402 in USDC on Base, and the result comes back into the module's trajectory. The function's docstring is what DSPy shows the model to decide when to use the tool, so write it plainly, and the wallet's spend limit is the backstop.
Payment stays deterministic
Here is the DSPy-specific point worth understanding, because DSPy is about optimizing programs. DSPy optimizers, like the few-shot and instruction optimizers, tune the prompts and demonstrations a module uses; they do not change the code inside a tool. So the payment logic, which lives in your function and the Node proxy, is untouched by optimization and stays deterministic. What the tool does when called is fixed; only when and how the module decides to call it can shift as the program is optimized.
That separation is healthy: you want the model's reasoning to improve with optimization, but you do not want payment behavior to be something an optimizer tweaks. Keep all payment logic in the tool function and the proxy, never in a prompt that an optimizer might rewrite, and the money path stays stable while the reasoning around it gets better. If you optimize a program that can pay, do it on testnet first so any change in how often the module reaches for the paid tool shows up as test USDC spend rather than real, and confirm the spend pattern is what you expect before moving to mainnet.
The earn side
To charge for what your DSPy program serves, expose it over HTTP and front it with a small Node gateway running createX402Plugin. The gateway returns the 402, verifies payment, and proxies paid requests to your program.
import Fastify from "fastify";
import { createClient } from "@blockchain0x/node";
import { createX402Plugin } from "@blockchain0x/x402/server/fastify";
const sdk = createClient({ apiKey: process.env.B0X_API_KEY! });
const app = Fastify();
await app.register(createX402Plugin, {
sdk,
defaultNetwork: "testnet",
pricing: { "POST /program/run": { amountUsdc: "0.10", payToAddress: process.env.B0X_PAYTO_ADDRESS!, paymentRequestId: "pr_program" } },
});
app.post("/program/run", async (req) => {
const r = await fetch("http://127.0.0.1:8000/run", { method: "POST", body: JSON.stringify(req.body) });
return await r.json();
});
await app.listen({ port: 8080 });An unpaid call gets a 402; a paid one runs your program. The program's Python code does not change; the gateway is the paywall. Keep a free discovery route so callers can see what the program does before paying.
Cost as part of the metric
DSPy optimizes against a metric, and when a program can spend money, cost is worth folding into that metric. If you optimize purely for answer quality, an optimizer may favor a program that reaches for the paid tool more often, because more data sometimes helps the score, quietly raising spend. You can counter that by having your evaluation metric account for cost, rewarding a program that hits the same quality with fewer paid calls.
You do not need anything exotic: track how many times the paid tool fired during an evaluation run and let the metric prefer cheaper trajectories at equal quality. Because the spend is in dollars, the cost term is in the same unit as any budget you set. This keeps optimization honest about money, so the program you ship is good and economical rather than good at any price, which matters precisely because DSPy is so willing to push the metric upward.
Compatibility
The integration works across DSPy's surfaces because it lives in a tool (pay) or a gateway (earn), not in the framework internals.
| DSPy surface | Works? | Notes |
|---|---|---|
dspy.Tool in dspy.ReAct |
Yes | The common pay path |
| Custom module calling a tool | Yes | Any module that invokes the tool works |
| Optimizers (few-shot, instruction) | Yes | They tune prompts, not the tool's payment code |
| Earning from a program endpoint | Yes | Front it with the Node gateway |
| Payment package language | Node | Python DSPy talks to it over localhost |
When this fits
The integration fits when a DSPy module needs to pay for what it calls, when you want to charge for what your program serves, or both, and you are comfortable running one or two small Node processes alongside the Python. That covers DSPy programs that touch money: one that pays for a data source mid-reasoning, one that sells a result, or both.
It fits less well when the program never touches paid resources or when a single human-approved payment is the only money movement. For per-call machine payments at agent scale, the dspy.Tool plus proxy, with an optional earn gateway, is the integration that fits DSPy's tool-using modules while keeping payment cleanly outside the optimized prompt.
Pricing
Integration is free; you build it from open packages. What you pay is the wallet platform fee per agent on the pricing page: Free is $0 per agent per month at a 5% transaction fee, Pro is $9 at 2%, Business is $29 at 1%. Per-agent pricing means each agent that transacts, paying or earning, is billed on its own wallet, so you pay for the agents that actually move money.
What to ship today
Wrap pay_and_fetch in a dspy.Tool, give it to a dspy.ReAct module, start a proxy with a sk_test_ key, and make one paid call on Base Sepolia. Keep all payment logic in the tool, not in a prompt, so optimization leaves it deterministic. To earn, front your program with the Node gateway. For sibling-framework overviews see crewai-payment-integration and agno-payment-integration. Pricing is on the pricing page.