@acedatacloud/x402-client
v2026.708.0
Published
X402 payment protocol client for AceDataCloud APIs
Readme
@acedatacloud/x402-client
X402 payment protocol client for AceDataCloud APIs. Pay per request with USDC — no API key, no account, no session.
Every AceDataCloud API that costs money (chat completions, image generation, video generation, music generation, web search, …) now speaks the x402 protocol. This package ships the only piece the SDK can't do by itself: signing an X-Payment header when the server returns 402 Payment Required. It plugs straight into @acedatacloud/sdk as a paymentHandler.
- 🟦 Base — USDC (ERC-20). Two schemes:
exact(EIP-3009transferWithAuthorization) for fixed-price endpointsupto(Uniswap Permit2PermitWitnessTransferFrom) for token-metered chat APIs — sign once over a ceiling, the facilitator settles the actual cost
- 🟪 Solana — USDC (SPL) via signed transfer
- 🟨 SKALE — USDC (bridged) via EIP-3009
All three networks settle through our production facilitator at https://facilitator.acedata.cloud (source).
How it works
SDK call (no Bearer token)
│
▼
x402.acedata.cloud ── 402 Payment Required + accepts[] ──▶ SDK
│
paymentHandler │ (this package)
▼
sign X-Payment envelope
│
◀────────────── retry with X-Payment ──────────────────┘
200 OK (+ x402_tx hash in headers)The SDK keeps doing everything it already does — task polling, SSE streaming, retries, typed errors — and this package contributes exactly one thing: the per-chain signature.
Install
npm install @acedatacloud/sdk @acedatacloud/x402-client
# extra peers depending on the chain(s) you use:
npm install ethers # Base / SKALE
npm install @solana/web3.js # SolanaIf the npm package is not yet published you can install from GitHub:
npm install @acedatacloud/sdk github:AceDataCloud/X402ClientQuick start
Base or SKALE (EVM)
import { AceDataCloud } from '@acedatacloud/sdk';
import { createX402PaymentHandler } from '@acedatacloud/x402-client';
const client = new AceDataCloud({
// No apiToken — per-request on-chain payment.
paymentHandler: createX402PaymentHandler({
network: 'base', // or 'skale'
evmProvider: window.ethereum, // any EIP-1193 provider works
evmAddress: '0xYourAddress...',
preferScheme: 'upto', // for chat APIs; falls back to 'exact' if upto isn't offered
}),
});
const res = await client.openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: 'Say hi in 3 words' }],
max_tokens: 10,
});
console.log(res.choices[0].message.content);When
preferSchemeis omitted the handler picks whatever the server lists first. Chat APIs advertise bothexactandupto; image / video / music APIs currently advertise onlyexact.
One-time Permit2 approval (upto only)
Before the first upto payment, the payer wallet must approve Permit2 to pull its USDC. Either run the bundled CLI:
cd typescript
npx tsx scripts/approve-permit2.ts --network base
# expects X402_PRIVATE_KEY or X402B_BASE_PAYER_PRIVATE_KEY in env / .claude/.envor call it programmatically:
import { approvePermit2 } from '@acedatacloud/x402-client';
await approvePermit2({
rpcUrl: 'https://mainnet.base.org',
privateKey: process.env.X402B_BASE_PAYER_PRIVATE_KEY!,
tokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
});The helper is idempotent — re-running it after the allowance is already at or above the requested amount returns { skipped: true } without sending a transaction. Required exactly once per (payer, token, chain).
Solana
import { AceDataCloud } from '@acedatacloud/sdk';
import { createX402PaymentHandler } from '@acedatacloud/x402-client';
const client = new AceDataCloud({
paymentHandler: createX402PaymentHandler({
network: 'solana',
solanaWallet: phantomWallet, // any wallet adapter with signTransaction()
}),
});
const task = await client.images.generate({
provider: 'nano-banana',
prompt: 'a yellow banana on a white background',
});
const result = await task.wait(); // task polling handled by the SDKEvery AceDataCloud endpoint the SDK exposes (openai.chat, images, audio, video, search, …) works the same way.
Setup
- Fund a wallet with USDC on the network you want to use:
- Base — USDC on Base
- Solana — USDC on Solana
- SKALE — bridged USDC on SKALE Europa
- A few cents of USDC is enough for most single requests (prices are listed at platform.acedata.cloud).
- Construct the handler with whatever wallet primitive you have: a browser provider, a Node
ethers.Wallet,@solana/web3.jskeypair, etc.
Low-level signing
If you need to produce an X-Payment envelope outside the SDK — for example in a custom fetch wrapper, tests, or an agent framework — the raw signing primitives are exported directly:
import {
signSolanaPayment,
signEVMPayment,
signEVMUptoPayment,
} from '@acedatacloud/x402-client';
// Solana
const solEnvelope = await signSolanaPayment(req, solanaWallet);
// Base / SKALE — exact scheme (fixed price)
const evmEnvelope = await signEVMPayment(req, evmProvider, evmAddress);
// Base — upto scheme (metered)
const uptoEnvelope = await signEVMUptoPayment(req, evmProvider, evmAddress);
const header = btoa(JSON.stringify(uptoEnvelope));The live e2e scripts under scripts/ use these low-level entry points to settle real USDC on chain. For application code, prefer the SDK path above.
Running the real end-to-end tests
This repo includes live, on-chain smoke tests. They require a funded wallet (a few cents of USDC is plenty).
git clone https://github.com/AceDataCloud/X402Client.git
cd X402Client
npm install
# Base — EVM private key, exact scheme
export BASE_TEST_PRIVATE_KEY=0x...
node --experimental-strip-types scripts/test-real-e2e.ts
# Base — EVM private key, upto scheme (chat API, metered)
export X402B_BASE_PAYER_PRIVATE_KEY=0x...
node --experimental-strip-types scripts/test-upto-e2e.ts
# Solana — base58-encoded secret key
export SOLANA_TEST_PRIVATE_KEY=...
node --experimental-strip-types scripts/test-solana-e2e.ts
# SKALE — EVM private key with funded bridged USDC
export SKALE_BASE_PRIVATE_KEY=0x...
node --experimental-strip-types scripts/test-skale-e2e.tsEach script:
- Sends
POST /openai/chat/completionswith no auth. - Parses the returned
402. - Signs the payment envelope.
- Retries with
X-Payment. - Prints the trace ID, chain tx hash, and the final chat response.
Dynamic pricing regression
scripts/test-api-billing-scenarios.ts runs multiple APIs with different payloads and verifies the advertised 402 amount exactly matches floor(credits × 0.095215 × 1e6), then asserts an ApiUsage record with the on-chain tx hash lands in our logging pipeline.
export X402B_BASE_PAYER_PRIVATE_KEY=0x... # Base wallet with a few cents of USDC
node --experimental-strip-types scripts/test-api-billing-scenarios.tsProof of real on-chain settlement
Latest successful live runs on 2026-05-31 (Base, upto scheme — three different metered chat models settled with the actual cost, not the ceiling):
claude-sonnet-4-5→0xc1f90cc6…b093dd8(151 atomic USDC)gpt-5.5→0xda8f0ff0…fcca57(135 atomic USDC)glm-4.7→0xae9bba18…7c5dda(262 atomic USDC)
Previous exact-scheme runs (2026-04-19):
- Base — tx
0x11313652…5327ec - Solana — tx
3qB25xsy…eBVzr - SKALE — tx
0xc6c7affe…aae42e1
All chains live; all settlements flow through our own facilitator at https://facilitator.acedata.cloud.
Pricing for other APIs
The handler does not hardcode AceDataCloud prices. For any x402-enabled API, the server returns the real charge in the first 402 response:
{
"accepts": [
{
"network": "base",
"maxAmountRequired": "95215",
"payTo": "...",
"asset": "..."
}
]
}That means:
- price is determined server-side by the API path, model, and request body
- different APIs can return different
maxAmountRequired - the client signs exactly what the server asks for
If you want to preview the price for another API without paying yet, send the same request once without Bearer auth and without X-Payment, then inspect the returned accepts list.
At the time of writing, 121 of 122 public APIs have x402 pricing configured — the only exception is the free /fish/voices listing.
Python
A byte-compatible Python port is published as acedatacloud-x402 and lives in ../python/ — same exact + upto signers, same approve_permit2 helper, same on-chain test vectors.
Release flow (CalVer)
This repo publishes on every push to main:
.github/workflows/publish.ymlbuilds, computes today's version via CalVer (YYYY.M.D[.N]), patchespackage.json, runsnpm publish --provenance --access public, and creates a matching GitHub Release.- The
versionfield committed inpackage.jsonis a placeholder — the real number is stamped at publish time.
If you need a manual publish:
npm run version:date # stamps today's date
npm publish --access publicLicense
MIT © AceDataCloud
