Python with x402
Python with x402
Section titled “Python with x402”Call Cerebrus Pulse from Python with the official x402 client, handling the payment yourself.
Installation
Section titled “Installation”pip install "x402[evm]" httpx eth-accountpip install "x402[svm]" "solana>=0.36,<0.37" "solders>=0.27,<0.28" httpxBuild an x402Client, register a payment scheme onto it with your signer, then
hand that client to the HTTP wrapper. The registration goes on the x402
client, not on the HTTP client. This is the x402 v2 client: it pays with the
PAYMENT-SIGNATURE header, the only one this API accepts. The client refuses any
payment above its spend cap, $1 by default; the setup below lowers it to $0.10.
import osfrom eth_account import Accountfrom x402 import x402Clientfrom x402.http.clients import x402HttpxClientfrom x402.mechanisms.evm.exact import register_exact_evm_client
# Load your private key securely (env var, keyfile, etc.)PRIVATE_KEY = os.environ["BASE_WALLET_PRIVATE_KEY"]
signer = Account.from_key(PRIVATE_KEY)
x402 = x402Client()register_exact_evm_client(x402, signer)x402.set_spend_controls({"max_amount_per_payment": "$0.10"}) # refuse any payment above $0.10import osfrom x402 import x402Clientfrom x402.http.clients import x402HttpxClientfrom x402.mechanisms.svm import KeypairSignerfrom x402.mechanisms.svm.exact import register_exact_svm_client
# Load your Solana private key (base58-encoded)PRIVATE_KEY = os.environ["SOLANA_WALLET_PRIVATE_KEY"]
signer = KeypairSigner.from_base58(PRIVATE_KEY)
x402 = x402Client()register_exact_svm_client(x402, signer)x402.set_spend_controls({"max_amount_per_payment": "$0.10"}) # refuse any payment above $0.10Async Usage (Recommended)
Section titled “Async Usage (Recommended)”x402HttpxClient takes the configured x402Client and handles the 402 flow for
you: it sends the request, reads the payment terms, signs, and retries.
import asyncio
BASE_URL = "https://api.cerebruspulse.xyz"
async def get_pulse(coin: str, timeframes: str = "1h,4h") -> dict: async with x402HttpxClient(x402) as client: resp = await client.get( f"{BASE_URL}/pulse/{coin}", params={"timeframes": timeframes} ) resp.raise_for_status() return resp.json()
async def main(): data = await get_pulse("BTC") print(f"BTC: {data['confluence']['bias']} (score: {data['confluence']['score']})")
asyncio.run(main())import asyncio
BASE_URL = "https://api.cerebruspulse.xyz"
async def get_pulse(coin: str, timeframes: str = "1h,4h") -> dict: async with x402HttpxClient(x402) as client: resp = await client.get( f"{BASE_URL}/pulse/{coin}", params={"timeframes": timeframes} ) resp.raise_for_status() return resp.json()
async def main(): data = await get_pulse("BTC") print(f"BTC: {data['confluence']['bias']} (score: {data['confluence']['score']})")
asyncio.run(main())Helper Functions
Section titled “Helper Functions”async def get_sentiment() -> dict: async with x402HttpxClient(x402) as client: resp = await client.get(f"{BASE_URL}/sentiment") resp.raise_for_status() return resp.json()
async def get_funding(coin: str, lookback_hours: int = 24) -> dict: async with x402HttpxClient(x402) as client: resp = await client.get( f"{BASE_URL}/funding/{coin}", params={"lookback_hours": lookback_hours} ) resp.raise_for_status() return resp.json()
async def get_bundle(coin: str, timeframes: str = "1h,4h") -> dict: async with x402HttpxClient(x402) as client: resp = await client.get( f"{BASE_URL}/bundle/{coin}", params={"timeframes": timeframes} ) resp.raise_for_status() return resp.json()Working with the Response
Section titled “Working with the Response”Field meanings are on the /pulse reference. Three to get right: funding rates are hourly,
open interest is in contracts (BTC for BTC, not USD), and confluence.score is a bullish vote share
(1 = all bullish, 0 = all bearish), not a confidence.
async def analyze_market(coin: str): data = await get_pulse(coin, timeframes="1h,4h")
price = data["price"].get("current") confluence = data["confluence"] regime = data["regime"]["current"]
print(f"\n{'='*50}") print(f"{coin} @ ${price:,.2f} | Regime: {regime}") print(f"Confluence: {confluence['bias']} (bullish share {confluence['score']})") print(f"{'='*50}")
for tf, analysis in data["timeframes"].items(): if "error" in analysis: print(f"\n [{tf}] {analysis['error']}") continue ind = analysis["indicators"] print(f"\n [{tf}] closed at {analysis['last_closed_candle_time']}") print(f" RSI: {ind['rsi_14']} ({ind['rsi_zone']})") print(f" Trend: {ind['trend']['label']} ({ind['trend']['ema_stack']})") print(f" BB Pos: {ind['bollinger']['position_pct']:.0%}") print(f" Z-Score: {ind['zscore_100']}")
deriv = data["derivatives"] oi_usd = deriv["open_interest"] * price # open interest is in contracts print(f"\n Funding: {deriv['funding_rate']:.7f}/h = {deriv['funding_annualized_pct']:.2f}% annualized") print(f" OI: {deriv['open_interest']:,.0f} {data['price_unit'].split()[-1]} (${oi_usd:,.0f})") print(f" Impact spread: {deriv['impact_spread_bps']:.2f} bps")Error Handling
Section titled “Error Handling”import httpx
async def safe_pulse(coin: str) -> dict | None: try: async with x402HttpxClient(x402) as client: resp = await client.get(f"{BASE_URL}/pulse/{coin}") resp.raise_for_status() return resp.json() except httpx.HTTPStatusError as e: if e.response.status_code == 400: print(f"Invalid input (not charged): {e.response.json().get('detail')}") elif e.response.status_code == 402: print("Payment not accepted, e.g. not enough USDC (nothing settled)") elif e.response.status_code == 404: print(f"Unknown coin: {coin} (GET /coins lists them)") elif e.response.status_code == 429: print("Rate limited — wait and retry") elif e.response.status_code == 503: print(f"Unavailable; retry after {e.response.headers.get('Retry-After')} s") elif e.response.status_code == 504: print("Engine timeout (not charged) — try again") else: print(f"Error {e.response.status_code}: {e.response.text}") return NoneMulti-Coin Scanning
Section titled “Multi-Coin Scanning”async def scan_all_coins(): # First, get the coin list (free) async with x402HttpxClient(x402) as client: resp = await client.get(f"{BASE_URL}/coins") coins = resp.json()["coins"]
print(f"Scanning {len(coins)} coins...")
# Then fetch pulse for each (paid) for coin in coins[:5]: # Limit to 5 for this example data = await safe_pulse(coin) if data: c = data["confluence"] print(f" {coin}: {c['bias']} (bullish share {c['score']})")- Reuse the client within a session to avoid reconnection overhead
- Use
/bundleto get/pulse, 24h/fundingand/sentimentin one request (one rate-limit slot instead of three); its page shows how its price compares - Check
/healthbefore batch operations to avoid wasting payments on a degraded gateway - Handle stale data —
meta.warningis"stale_data"when the price feed is more than 15 minutes old, andmeta.warningslists stalled inputs such as"stale_candles"
Related
Section titled “Related”- Quickstart — Make your first API call in 5 minutes
- x402 Payments Guide — How micropayments work, wallet setup, and supported chains
- GET /pulse/{coin} — Full endpoint reference for the pulse endpoint
- GET /screener — Market-wide scanning endpoint reference
- Response Schemas — Detailed field descriptions and value ranges