Skip to main content
An agent with an API key can spend whatever the key can spend. That is fine when a person is watching it and awkward when nobody is. The usual fixes live outside the agent, in a dashboard or a billing alert that tells you about the problem after it happened. Venice supports a second way in. Instead of a key, the agent holds a wallet. It authenticates by signing a message, pays for each request out of a USDC balance attached to that wallet address, and every charge lands in a ledger it can read back. There is no account, no dashboard, and no key to leak. The ceiling is the balance, and you decide what to put there. This guide builds an agent that does exactly that, under a budget it enforces on itself.

Run this notebook in Google Colab

Every step below as an executable notebook. It runs without a funded wallet and stops at the payment wall, so you can see the whole flow before spending anything.

How It Works

Four moving parts, three of which are just HTTP: Inference itself is the ordinary /chat/completions call. The only difference is which header you send.

What It Costs to Start

Two numbers matter and they are not the same number. The minimum top-up is five dollars. That is the smallest amount /x402/top-up will settle, and it is returned in the discovery response rather than hardcoded anywhere, so read it rather than trusting this page. The minimum balance to make a call is ten cents. A wallet holding less than that gets a 402 back from inference even though it holds money. So five dollars is the smallest wallet worth funding, and five dollars is what this guide gives the agent. Worth knowing what that buys: a short question to qwen3-5-9b costs about twenty seven input tokens and twenty six output tokens, which at that model’s prices is roughly seven millionths of a dollar. Five dollars is on the order of three quarters of a million questions. The budget here is not a tight constraint, it is a blast radius.

Setting Up

The x402 SDK does the payment signing. Do not hand-roll it: the transfer authorization is EIP-712 typed data and a reused nonce fails verification in ways that are tedious to debug.
The x402 Python SDK requires Python 3.10 or newer. Colab is fine. A system Python that shipped with macOS may not be.
Create agent.py with the configuration. BUDGET_USD is the ceiling the agent enforces on itself, set here to the whole wallet. Lower it and the agent stops before the money does, which is the only knob you are likely to change.

A Wallet the Agent Owns

The agent needs a keypair. In production this is a wallet you funded deliberately and whose key lives in a secret manager. While you are building, generating a throwaway is the right move, because a wallet with no money cannot do anything expensive by accident.
Keep the private key out of the notebook. In Colab, put it in Secrets and read it with userdata.get("WALLET_KEY").

Signing In Instead of Authenticating

There is no key to send, so each request carries a proof that the wallet owner made it. The proof is an EIP-4361 message, signed, then base64 encoded into the SIGN-IN-WITH-X header. The message format is exact. Venice rebuilds these bytes on its side and verifies your signature against them, so a stray blank line means a rejected signature rather than a helpful error.
Three rules govern these headers, and all three exist to stop replay. The signature is valid for five minutes from Issued At. Each nonce is single use for about five and a half minutes. And the signer must match the wallet in the path, so one wallet cannot inspect another and gets a 403 for trying. The practical consequence is that you sign a fresh header per request rather than caching one. Signing is local and free, so this costs nothing.
On a fresh wallet:
canConsume is the field to branch on. It accounts for the ten cent floor, so you do not have to.

Putting Money In

Topping up is two requests. The first asks what Venice accepts and is unauthenticated, because there is nothing to authenticate yet. The second carries a signed transfer authorization.
Discovery returns one entry per rail. Base and Solana today:
Two details in there are easy to walk past. amount is in base units, and USDC has six decimals, so 5000000 is five dollars and not five million of anything. On the Solana rail, extra.feePayer is a Venice operated account that covers the transaction fee, which is what lets a wallet pay without holding SOL.
The default spend control is the first thing that will stop you. The SDK ships with max_amount_per_payment set to one dollar, and the Venice minimum top-up is five, so an unmodified client rejects every rail on offer and raises NoMatchingRequirementsError before it ever contacts the network. Raise the cap deliberately rather than turning spend controls off.
Settling from a wallet with no USDC returns a 400 with PAYMENT_VERIFICATION_FAILED. That is the expected shape of failure: the signature was fine and the transfer was not.

Paying Per Call

With a balance in place, inference is a normal request that happens to carry a signature. Turning off the Venice system prompt matters more than it looks: it is worth about seventeen hundred input tokens per call, which is two orders of magnitude more than the question itself.
Handling 402 as a normal outcome rather than an exception is the whole design. An agent paying its own way will run out of money eventually, and running out of money is not a crash.

Reading What It Spent

The ledger is authoritative. Rather than estimating from token counts, ask what was actually charged.
Each row links back to the call that caused it:
TOP_UP and REFUND rows appear here too, with positive amounts. Filtering to CHARGE gives you spend.

The Budgeted Run

Now the loop. Before each call the agent checks what it has spent, and it declines to start work it cannot pay for.
Run it with the full five dollars and the budget never binds, which is the honest result at these prices. To watch the ceiling actually work, set one that a single call will breach:

Where This Leaves You

The agent holds its own money, proves its identity with a signature, and cannot exceed a limit you set, all without an account existing anywhere. For a scheduled job, a serverless function, or anything you would rather not hand a long-lived key to, that is a materially different security posture. Some things worth doing next:

Cap it in the protocol

Spend controls in the SDK are per payment, not per session. Pair them with the budget loop above so a bug in one cannot defeat the other.

Refill on empty

Catch the 402, top up, and retry. This is what venice-x402-client does for you on the TypeScript side.

Pay on Solana

Same flow, different rail. Sign Ed25519 and set the returned feePayer so the wallet needs no SOL.

Give it real work

Swap the task list for a tool-calling loop and the ledger starts showing you what each decision cost.
For the full endpoint reference, see x402 top-up and Using x402 with the Venice API.