How to Build a Private API Client With Ethereum zkAPI
Learn how Ethereum's zkAPI separates API payment identity from usage with zero-knowledge proofs, including local builds, browser SDK setup, testing, and privacy limits.
On this page
Ethereum's new zkAPI implementation turns an on-chain deposit into private API spending credits, and the practical part is now available as an open-source client and browser SDK. The project launched on Ethereum mainnet on October 1, 2026, using zero-knowledge proofs to separate the wallet that funds API usage from the requests that consume it. This tutorial walks through the repository, builds the active workspace, explains the browser SDK, and shows where the privacy boundary actually sits.
What zkAPI changes about API payments
A normal paid API usually starts with an account or API key. That key identifies the customer, while the provider can associate requests with the account and its billing history. zkAPI changes the payment side of that relationship: a user funds a private note and later proves that the note can pay for a request without revealing which deposit belongs to the request.
The proof is generated locally. A zero-knowledge proof lets the client demonstrate that a statement is true without revealing the private information used to prove it. In zkAPI, that statement includes the user's authorization to spend from an active private balance. The server verifies the proof, checks that its state has not already been spent, and signs the next private state.
The current implementation uses Groth16 over the BN254 curve, Poseidon hashing, Baby-JubJub commitments and Schnorr signatures, with a 32-level Merkle tree for active notes. Those details matter because the browser assets, proving keys, verifier and deployment must belong to the same protocol revision. The current circuit revision is zkapi-v2-note-bound-v1.
Prepare the zkAPI development environment
The repository currently expects Rust, Node.js 24 or later, and Foundry for local development. You do not need to generate new zero-knowledge setup keys merely to build or consume the existing implementation; the repository includes the committed proving assets required by the active deployment.
Clone the repository and enter its root directory. Then install the JavaScript dependencies and build the Rust workspace. The important distinction is that this is a full protocol repository rather than a small JavaScript-only package, so the first build also validates the native components used by the proof system.
npm ci
cargo build --release --workspace --locked
cargo test --release --workspace --locked
cargo test --release --manifest-path protocol/rust/Cargo.toml --workspace --locked
Next, build the browser assets and run the SDK checks. These commands verify the browser-facing part of the implementation rather than only compiling the Rust services.
npm run test:sdk
npm run build:browser
npm run build:mainnet
npm run verify:assets
If you are working on the Solidity vault and verifier, change into the contract directory and run the Foundry test suite.
cd protocol/contracts
forge test
A successful build gives you a useful checkpoint: the Rust runtime, browser SDK, proof assets and contract tests have all passed their respective build or test stages. The repository does not require Cairo, a STARK prover, or submodule initialization for this workflow.
Keep the proof setup matched to the deployment
This is the step that is easy to skip and potentially costly to misunderstand. zkAPI's proving keys and verification components are not interchangeable. The browser proving assets, verifier, vault and server signing configuration must agree on the same circuit and deployment.
The repository identifies the active circuit as zkapi-v2-note-bound-v1. The checked-in setup under protocol/setup/v2 belongs to that circuit. If you are consuming the existing deployment, keep those assets together rather than generating replacements.
Do not run the setup command just to use the existing deployment. Running zkapi setup creates a new proving setup and therefore produces keys that are incompatible with the already configured verifier and deployment.
A fresh setup is appropriate only when you deliberately intend to create a separate deployment. In that case, the project provides an explicit command that writes the new setup into another directory.
./target/release/zkapi setup --output-dir protocol/setup/new-deployment
The project documentation also makes an important security qualification: the checked-in setup uses a single-party setup rather than a multiparty ceremony. Matching hashes can establish that you are using the intended artifacts, but they do not independently establish the security assumptions behind their original setup.
Initialize the browser SDK before creating a wallet
The browser SDK is the part of zkAPI that applications use when they want private payment state inside a web application. It owns the private note state, proof generation, wallet persistence, transaction journals, settlement and withdrawal recovery. The host application supplies the user interface and an EIP-1193 wallet provider, which is the standard JavaScript interface used by browser wallet extensions and other Ethereum-compatible wallets.
Configure the SDK before initializing its client. The configuration points the browser toward the static configuration file and the WebAssembly worker used to generate proofs.
import { configureBrowserSdk } from '@openanonymity/zkapi-browser-sdk';
import client from '@openanonymity/zkapi-browser-sdk/client';
configureBrowserSdk({
configUrl: '/zkapi/browser-config.json',
workerUrl: '/zkapi/assets/zkapiWasmWorker.js',
});
await client.init();
const unsubscribe = client.subscribe(snapshot => {
renderBalance(snapshot);
});
The important part of this sequence is its order. The SDK must know which deployment and proof assets it is using before the wallet is initialized. The subscription then gives the application a way to react to wallet state and display the current private balance.
Let the SDK handle the private note
A zkAPI wallet is not simply an Ethereum address with a balance displayed in a different interface. The client maintains private information that includes the note metadata, secret material, balance, blinding information, state anchor and server signature. The browser uses that state to construct authorization proofs without exposing the underlying note to the API authorization server.
When a request is authorized, the client proves that its current private state is valid and sufficiently funded. The proof does not reveal the note identifier, deposit, exact balance or other private state included in the witness. A nullifier is also produced so that the server can detect an attempt to reuse the same spending state.
The server then verifies the proof and signs the next state. This is why a developer should treat the SDK's private state as part of the wallet itself rather than as ordinary application data. Losing or incorrectly replacing that state can affect recovery and settlement.
Understand the API request path before connecting an app
The complete flow has several distinct pieces. The browser wallet holds the private state and runs the proof generation code. The server verifies authorization and handles usage settlement. An indexer tracks the vault's active note tree and provides the Merkle paths needed for proofs. The Ethereum vault handles deposits and withdrawals on-chain.
The result is deliberately different from a conventional payment gateway. The API authorization server can determine that a valid private balance authorizes a request, but it does not need the public deposit address that originally funded that balance. The blockchain still records deposits and withdrawals because those are on-chain operations.
For AI services, the current implementation can issue short-lived OpenRouter runtime keys. In the direct runtime-key mode, the user's prompts and responses can travel between the local client and the inference provider without passing through the zkAPI authorization server. That separation is a major part of the design rather than an incidental networking detail.
Test the proof system without funding a real wallet
You do not need to begin by sending real funds to test the cryptographic workflow. The repository includes an end-to-end acceptance test that exercises deposit, HTTP lease issuance, mocked provider usage, signed settlement, stale withdrawal handling and an honest withdrawal on a fresh local chain.
npm run test:e2e:v2
The provider and oracle are mocked in this acceptance environment, while the protocol services, wallet proofs and contracts are exercised for real. That distinction makes the test useful for checking the mechanics of the implementation without treating a local acceptance run as evidence that a production deployment has been independently audited.
You can also run the browser proof round-trip example from the Rust protocol package:
cargo run --release --locked --manifest-path protocol/rust/Cargo.toml \
-p zkapi-proof --example wasm_roundtrip -- \
"$PWD/scripts/prove-browser-fixture.mjs" "$PWD/sdk"
This checks the browser WebAssembly proving path against the native verifier. It is particularly useful when changing browser assets, because a successful JavaScript build alone does not prove that the generated artifacts remain compatible with the verifier.
Connect an OpenAI-compatible application through the local client
zkAPI also provides a native local client for applications that already speak an OpenAI-compatible API. This means you can place the zkAPI client between an existing application and the inference service without rewriting the application's request format.
The repository describes the local client as the wallet, proof-generation and OpenAI-compatible gateway component. The local service can expose chat-completions and responses-style interfaces, allowing existing software to communicate with it using familiar request structures.
That architecture is useful because the application does not have to understand Merkle paths, Groth16 proofs or private note state. Those responsibilities remain inside the zkAPI client. The application continues making model requests while the client handles authorization and settlement around them.
Know what zkAPI does not hide
The privacy boundary is narrower than the phrase "private API" might suggest. zkAPI is designed to separate payment identity from API usage; it does not make the API request itself invisible to the service receiving it.
The inference provider still sees the prompt and response. Network information such as an IP address and request timing can also remain observable. A provider may be able to correlate sessions through repeated personal information, writing patterns, conversation context or distinctive project material even when the payment source is unlinkable.
The public blockchain also continues to expose deposits and withdrawals. Zero-knowledge proofs hide selected relationships between those events and later spending, rather than turning the entire transaction history into private data.
The useful mental model: zkAPI separates who funded the usage from what the provider receives. It does not automatically hide what the provider receives, where the network request originates, or every public blockchain event surrounding the wallet.
Check these details before moving beyond local testing
Before funding a deployed wallet, verify that the application, browser assets, vault, server signing keys and circuit revision all refer to the same deployment. The project's documentation specifically calls for checking the circuit identifier, artifact hashes, chain identifier, vault address and signing keys against trusted deployment values.
Do not treat a successful local proof as proof that a production configuration is correct. A proof can be mathematically valid while the application points at the wrong deployment, and compatible-looking artifacts can still belong to different setups. The deployment manifest is therefore part of the security boundary, not merely configuration boilerplate.
Finally, remember that the current implementation is experimental. The project has separate documentation for its cryptography, protocol, API and deployment assumptions, and those assumptions include the single-party proving setup and the fact that the current Groth16 and Baby-JubJub construction is not designed as a post-quantum system.
For a developer, that makes the most useful next step fairly concrete: build the repository, run the local end-to-end tests, integrate the browser SDK against the matching deployment configuration, and inspect the proof and settlement lifecycle before putting real funds behind an application. The interesting part of zkAPI is not merely that Ethereum can pay for an API; it is that the payment authorization can become a separate cryptographic layer from the API request itself.
Written by


