x402 in Python: pay per request APIs from a script

Most x402 examples on the internet, including most of ours, are written in Node, because the @x402/fetch wrapper makes payment invisible in about four lines of JavaScript. But the people who most want per-request data live in Python: analysts in notebooks, scrapers-turned-buyers, agent builders on Python frameworks. So here is the honest Python guide to calling an x402 API, using our own Social Stats API as the live target, in three levels of depth: see the challenge, pay it with a client library, and understand what the library signs on your behalf.

Level 1: see the 402 with requests

An x402 endpoint is just HTTP, so plain requests shows you everything about the deal before any crypto is involved:

import requests

r = requests.get(
    "https://x402.findclout.com/api/v1/instagram/user",
    params={"username": "nba"},
)
print(r.status_code)   # 402
print(r.json())

The body is a machine readable offer:

{
  "x402Version": 2,
  "error": "Payment required",
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "maxAmountRequired": "10000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0xD5997d52769Fc6abd86B0E6d0f0e85F31F118bd8",
      "resource": "https://x402.findclout.com/api/v1/instagram/user",
      "description": "Instagram profile lookup",
      "mimeType": "application/json",
      "maxTimeoutSeconds": 60
    }
  ]
}

Read it like a price tag: the exact scheme means a fixed price, eip155:8453 is Base mainnet, the asset is the USDC contract on Base, and 10000 atomic units at USDC's six decimals is one cent. Your job as a client is to sign an authorization matching those terms and retry the request with the payment attached.

Level 2: pay it with a client library

Do not hand-roll the payment header. The x402 ecosystem ships a Python package, x402, which includes client helpers that wrap httpx or requests the way @x402/fetch wraps fetch: they intercept the 402, sign, retry, and hand you the paid response. The shape looks like this:

pip install x402 eth-account
import os
from eth_account import Account
from x402.clients.requests import x402_requests

account = Account.from_key(os.environ["AGENT_PRIVATE_KEY"])
session = x402_requests(account)

r = session.get(
    "https://x402.findclout.com/api/v1/instagram/user",
    params={"username": "nba"},
)
print(r.json())   # {"success": true, "followers": 89214550, ...}

One honest caveat: the x402 protocol moved from version 1 to version 2 recently, and client packages have been updating on their own schedules. Our server speaks x402 version 2, so check the package's current README for the exact import paths and confirm it supports v2 challenges before you build on it. If you hit a wall, the most battle-tested client today is still Node's @x402/fetch, and a perfectly pragmatic Python setup is a ten line Node sidecar script that your Python code calls with subprocess, unglamorous but reliable. The Node client itself is covered step by step in the agent wallet tutorial.

Level 3: what actually gets signed

Under every client library is the same primitive: an EIP-3009 TransferWithAuthorization signature. It is an EIP-712 typed message that says "move 10000 units of my USDC to this address, valid for the next 60 seconds," signed locally with your key. No transaction is sent by you; the facilitator submits it on-chain and pays the gas, which is why your wallet needs zero ETH. In Python, the core of it looks like this with eth-account:

import os, time, secrets
from eth_account import Account
from eth_account.messages import encode_typed_data

acct = Account.from_key(os.environ["AGENT_PRIVATE_KEY"])
now = int(time.time())

typed = {
    "types": {
        "EIP712Domain": [
            {"name": "name", "type": "string"},
            {"name": "version", "type": "string"},
            {"name": "chainId", "type": "uint256"},
            {"name": "verifyingContract", "type": "address"},
        ],
        "TransferWithAuthorization": [
            {"name": "from", "type": "address"},
            {"name": "to", "type": "address"},
            {"name": "value", "type": "uint256"},
            {"name": "validAfter", "type": "uint256"},
            {"name": "validBefore", "type": "uint256"},
            {"name": "nonce", "type": "bytes32"},
        ],
    },
    "primaryType": "TransferWithAuthorization",
    "domain": {
        "name": "USD Coin",
        "version": "2",
        "chainId": 8453,
        "verifyingContract": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    },
    "message": {
        "from": acct.address,
        "to": "0xD5997d52769Fc6abd86B0E6d0f0e85F31F118bd8",  # payTo from the 402
        "value": 10000,
        "validAfter": 0,
        "validBefore": now + 60,
        "nonce": "0x" + secrets.token_bytes(32).hex(),
    },
}

signed = acct.sign_message(encode_typed_data(full_message=typed))
print(signed.signature.hex())

That signature, plus the authorization fields, is what a client library packages into the payment header on the retried request. We show it so the flow is not magic, and so you can audit what you are authorizing: a fixed value, to a fixed address, expiring in 60 seconds. We still recommend the library for production, because the header encoding is versioned protocol surface and the maintained clients track it for you.

A real Python workload

Once session.get pays transparently, per-request data drops straight into normal Python workflows. A notebook cell that scores a list of creators:

urls = [
    "https://www.instagram.com/reel/DLmR4kXtQ2a/",
    "https://www.tiktok.com/@nflstreetmoments/video/7401882236711",
    "https://x.com/AdamSchefter/status/1812340221459871",
]

rows = []
for u in urls:
    r = session.get("https://x402.findclout.com/api/v1/post", params={"url": u})
    d = r.json()
    if d.get("success"):
        rows.append({"platform": d["platform"], "views": d["views"],
                     "like_rate": d["likes"] / d["views"]})

import pandas as pd
print(pd.DataFrame(rows))

Three lookups, three cents, live numbers. Billing is automation-friendly by design: bad URLs return HTTP 400 and 4xx responses are never charged, so a bug in your URL list costs nothing. The pattern scales into scheduled jobs cleanly, and the cron-agent version with budget math is written up in building a social listening agent with a wallet. For the protocol theory behind all of this, start with what is x402.

Why does a social stats API care this much about Python developers? Because we are the heaviest user of our own endpoints. FindClout verifies delivered views for brand placements across our American meme page network, the largest independently owned one in the country, spanning sports, finance, entrepreneurship, movies, TV, and music, and plenty of that verification tooling is Python scripts exactly like the ones above. If your scripts prove you need reach and not just measurements of it, that is the other half of our business.

Want the views, not just the numbers?

FindClout runs the biggest independently owned American meme page network, with sports, finance, entrepreneurship, movie, TV, and music pages reaching real American audiences. We place brands natively inside viral clips people already love: not ads next to content, your product inside it.

Book a call at findclout.com