X402 Python SDK Integration Tutorial

The Python SDK is suitable for backend services, data tasks, automation agents, and batch processing scripts. acedatacloud is responsible for API calls, while acedatacloud-x402 is responsible for signing the PAYMENT-SIGNATURE request header.

Source code and package addresses:

Install Dependencies

pip install acedatacloud acedatacloud-x402

If you want to use upto, you also need to call the Permit2 approve CLI once, which depends on web3:

pip install 'acedatacloud-x402[cli]'

Output of clean Python venv installation and import check:

acedatacloud 2026.4.26.1
acedatacloud-x402 2026.5.31.3
imports_ok True True True True True True
usage: acedatacloud-x402 [-h] {approve-permit2} ...
approve-permit2  One-time ERC-20 approve(Permit2, amount) needed before signing upto payments.

Result explanation:

  • Both acedatacloud and acedatacloud-x402 can be installed and imported from PyPI.
  • pip install 'acedatacloud-x402[cli]' will include the approve-permit2 CLI for pre-authorization of upto.

Base or SKALE Example

The following example does not require an API Token. The wallet private key is only used for local signing and will not be sent to YuJun.

import os

from acedatacloud import AceDataCloud
from acedatacloud_x402 import EVMAccountSigner, create_x402_payment_handler

signer = EVMAccountSigner.from_private_key(os.environ["EVM_PRIVATE_KEY"])

client = AceDataCloud(
    payment_handler=create_x402_payment_handler(
        network="base",
        evm_signer=signer,
    )
)

res = client.openai.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Say hi in 3 words"}],
    max_tokens=10,
)

print(res["choices"][0]["message"]["content"])

The current return of the Python SDK is a dict, so the example uses res["choices"][0]["message"]["content"]. Do not directly assume it has a .choices attribute.

The program run result of SKALE paid call:

payer 0xd0479FA9FD8C678303d477433d24C15e3723CC1C
elapsed_ms 4786
content ADC_PY_SDK_X402_OK
id chatcmpl-DlcWajqAHOop3iebmO19XRfT5bTPz

Result explanation:

  • The program completed the 402 parsing, PAYMENT-SIGNATURE signing, and original request retry.
  • content ADC_PY_SDK_X402_OK is a fixed string returned by the model, indicating that the request has entered the upstream API through the X402 payment link.
  • id chatcmpl-DlcWajqAHOop3iebmO19XRfT5bTPz is the response ID for this chat completion.

When using SKALE, simply change the network name:

client = AceDataCloud(
    payment_handler=create_x402_payment_handler(
        network="skale",
        evm_signer=signer,
    )
)

Solana Example

Solana uses a base58 encoded secret key:

import os

from acedatacloud import AceDataCloud
from acedatacloud_x402 import SolanaKeypairSigner, create_x402_payment_handler

client = AceDataCloud(
    payment_handler=create_x402_payment_handler(
        network="solana",
        solana_signer=SolanaKeypairSigner.from_base58(os.environ["SOLANA_SECRET_KEY"]),
    )
)

res = client.openai.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Say hi in 3 words"}],
    max_tokens=10,
)

The Solana path will construct and submit an SPL USDC TransferChecked transaction, then place the transaction signature into the PAYMENT-SIGNATURE envelope.

The Solana paid retry has returned HTTP 200 and ADC_SOLANA_E2E_OK on the production API. This public RPC query encountered rate limiting and did not stabilize the confirmation of the on-chain signature; please use your own Solana RPC to query this transaction when reconciliation is needed.

Async Client

The same payment handler can be used for AsyncAceDataCloud:

import os

from acedatacloud import AsyncAceDataCloud
from acedatacloud_x402 import EVMAccountSigner, create_x402_payment_handler

signer = EVMAccountSigner.from_private_key(os.environ["EVM_PRIVATE_KEY"])

client = AsyncAceDataCloud(
    payment_handler=create_x402_payment_handler(
        network="base",
        evm_signer=signer,
    )
)

res = await client.openai.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Say hi in 3 words"}],
    max_tokens=10,
)

Using upto for Post-Measurement

The actual cost of APIs such as chat completions and model calls may only be known after the response ends. At this point, the API may return both exact and upto. If you want to prioritize using upto:

client = AceDataCloud(
    payment_handler=create_x402_payment_handler(
        network="base",
        evm_signer=signer,
        prefer_scheme="upto",
    )
)

The program run result for Base upto:

payer 0x5d4f08D5c2bb60703284bc06671Eb680fA41B105
elapsed_ms 5104
content ADC_BASE_UPTO_OK
id chatcmpl-DlcbyS4IT8kUAMo4Ri97HiIHc9T8V
settlement tx 0x4b0b836ce1cd1171cdbc37df1637150b024214ec28e7f6f2d09122f15cbfc036
settled value 3 atomic USDC

On-chain confirmation:

explorer https://basescan.org/tx/0x4b0b836ce1cd1171cdbc37df1637150b024214ec28e7f6f2d09122f15cbfc036
block 46726437
transfer value 3 atomic USDC

Result explanation:

  • content ADC_BASE_UPTO_OK indicates that the request has truly entered the model API.
  • settled value 3 atomic USDC indicates that upto is settled based on actual usage, rather than deducting the full limit.
  • settlement tx can be opened on BaseScan; save the tx hash, payer, completion id, and request summary for reconciliation.

upto uses Permit2 to authorize a limit, and the actual settlement amount cannot exceed this limit. Before the first use, you need to perform a one-time approve(Permit2, amount) on the USDC on the target chain.

CLI method:

X402_PRIVATE_KEY=0x... acedatacloud-x402 approve-permit2 --network base

Program method:

from acedatacloud_x402 import EVMAccountSigner, approve_permit2

approve_permit2(
    rpc_url="https://mainnet.base.org",
    signer=EVMAccountSigner.from_private_key("0x..."),
    token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
)

This helper is idempotent. If the allowance is already sufficient, it will return {"skipped": true} and will not repeat the on-chain transaction.

Low-Level Signing

If you do not use the SDK, you can directly call the low-level signing functions:

import base64
import json

from acedatacloud_x402 import EVMAccountSigner, sign_evm_payment

envelope = sign_evm_payment(requirement, EVMAccountSigner.from_private_key("0x..."))
x_payment = base64.b64encode(json.dumps(envelope, separators=(",", ":")).encode()).decode()

Low-level functions are suitable for testing, proxy layers, gateway integration, or unofficial SDKs. Regular business code should prioritize using create_x402_payment_handler.