payperbyte-sdk — PayPerByte Python SDK
Python SDK for PayPerByte — the cryptographically attested, provenance-verifiable data layer for AI agents. Discover first-party feeds, pay per call, subscribe, stream payloads, and verify every payload against its EIP-712 attestation (authenticity + tamper-evidence — who signed these exact bytes — not a correctness guarantee). No token; x402 USDC payments settle on Base mainnet (the on-chain subscribe + EIP-712 attestation rail is Arbitrum Sepolia, testnet, pre-audit).
Installation
pip install payperbyte-sdk
Keyless x402 pay-per-call support (the GatewayClient) needs the optional x402 stack:
pip install "payperbyte-sdk[x402]"
Quick Start
from eth_account import Account
from byte import (
Publisher,
Subscriber,
Mercat,
GatewayClient,
verify_payload,
HashMismatchError,
CanonicalFormMismatchError,
ARBITRUM_SEPOLIA,
)
# 1. Discover — browse first-party feeds via the keyless x402 gateway catalog.
gw = GatewayClient(account=Account.from_key("0x...")) # a wallet, NOT an API key
catalog = gw.discover() # GET /feeds
for feed in catalog["feeds"]:
print(feed["id"], feed["price"], feed["provenance"])
# Or discover publishers via the indexer (Mercat).
mercat = Mercat(ARBITRUM_SEPOLIA.indexer_url)
publishers = await mercat.search(topic="eth-price")
# 2. Subscribe — register in the social registry and approve DataStream to pull
# per-message fees directly. No escrow, no deposit: your USDC stays in your
# wallet until a message is actually settled. allowance_usdc is a 6-decimal
# spend ceiling sized to cover the fees you expect to pay.
# (This leg is on Arbitrum Sepolia — testnet, pre-audit.)
subscriber = Subscriber("0x...private_key...", ARBITRUM_SEPOLIA)
subscriber.subscribe(publishers[0]["address"], allowance_usdc=10.0)
# 3. Stream — receive payload events as the publisher broadcasts.
async for msg in subscriber.stream():
payload = fetch_from_my_archive(msg["payload_hash"])
# 4. Verify — keccak256 of the EXACT delivered bytes vs the attested hash.
# Throws HashMismatchError if the bytes don't match what was attested.
try:
verify_payload(payload, msg["payload_hash"])
except HashMismatchError:
continue # do NOT consume mismatched bytes
consume(payload)
Keyless x402 (pay-per-call)
The GatewayClient mirrors the PayPerByte x402 gateway. It is keyless: a wallet signs
the payment (EIP-3009 transferWithAuthorization, gasless — the facilitator broadcasts and
pays gas), settled in USDC on Base mainnet. There is no API key anywhere.
from eth_account import Account
from byte import GatewayClient
gw = GatewayClient(account=Account.from_key("0x...")) # defaults to https://x402.payperbyte.io
result = gw.fetch_feed("weather") # GET -> 402 -> sign USDC -> retry -> data
print(result["data"])
print(result["settlement"]) # {"success", "payer", "transaction"} (on-chain settle tx) or None
print(result["disclaimerCategory"])
Verdict oracles are POST feeds that take a JSON body and answer BEFORE you act — a signed ALLOW/WARN/BLOCK with an embedded EIP-712 receipt over the exact answer bytes:
result = gw.fetch_feed("address-reputation", body={
"domain": "example.com", # the payee's web domain
"address": "0x1111111111111111111111111111111111111111", # receiving address
"chain": "base",
})
# result["data"]["answer"]["verdict"] -> "ALLOW" | "WARN" | "BLOCK"
# result["data"]["attestation"] -> EIP-712 receipt: recompute keccak256(answer),
# recover the signer, THEN act
(pkg-verdict does the same for software packages.) The verdict is a screening signal —
the receipt proves who signed these exact bytes, not that the verdict is correct.
Two distinct USDC flows. The on-chain settlement leg (
Subscriber.subscribe→ register in DataRegistry + approve DataStream as a direct USDC spender, Arbitrum Sepolia) is independent of the x402 gateway payment (GatewayClient→ EIP-3009 USDC on Base at fetch time). Pay-per-call feeds need only the x402 leg.
Features
- Feed discovery — browse the x402 gateway catalog (
GatewayClient.discover) or search publishers via the indexer (Mercat) - Subscription management — subscribe, unsubscribe, check status (direct-allowance USDC settlement; the SDK approves DataStream as a direct spender)
- Data streaming — publish and receive payloads via DataStream
- Payload verification — byte-exact
keccak256against the EIP-712 attested hash, plus a form-aware archive path that fails closed (CanonicalFormMismatchError) instead of raising a tamper alarm it cannot prove - Keyless x402 — pay-per-call feed access with a wallet (EIP-3009, USDC on Base), no API key
- Provenance — read publisher status, subscriber/message counts, and revenue from the on-chain registry
Network Support
| Network | Chain ID | Role | Status |
|---|---|---|---|
| Base | 8453 | x402 USDC payment settlement (GatewayClient) |
Live (mainnet) |
| Arbitrum Sepolia | 421614 | On-chain subscribe + EIP-712 attestation anchor | Live (testnet, pre-audit) |
| Arbitrum One | 42161 | Attestation mainnet re-anchor | Planned (audit-gated) |
PayPerByte contracts
PayPerByte is a lean 3-contract core. No token; all settlement is in external USDC. Subscriptions are a direct ERC-20 allowance — there is no escrow contract. A subscriber registers in DataRegistry and grants DataStream a USDC allowance; DataStream pulls the exact per-message fee with transferFrom at publish time, so funds stay in the subscriber's wallet until a message is settled. Each payload carries an EIP-712 PayloadAttestation so subscribers can confirm exactly what they received and from whom.
| Contract | Role |
|---|---|
| DataRegistry | Publisher registration; subscriber social registry (subscribe / unsubscribe / isSubscribed) |
| DataStream | Per-message payload settlement; pulls fees via direct USDC allowance |
| SchemaRegistry | Feed schema + methodology references |
Contract and settlement-USDC addresses are resolved per-network by the SDK (ARBITRUM_SEPOLIA, LOCAL_ANVIL).
Canonical payload bytes — two forms, and why byte-exact verification wins
The primary verify path is byte-exact: hash the exact bytes you received
(verify_payload) against the attested hash. That path needs no canonicalization at all
and is the strongest tamper evidence the SDK offers. Prefer it whenever you hold the
delivered bytes.
Canonicalization only enters when a payload is re-serialized (e.g. re-deriving bytes from a parsed archive envelope) — and the stack has two canonical-JSON forms there, not one:
- SDK publish path (
byte.canonical): recursively key-sorted, no whitespace,ensure_ascii=False. Matches the TypeScript SDK for payloads that keep values to strings/bools/ints; floats, huge ints, and non-BMP keys are explicitly out of scope (this is NOT full RFC 8785/JCS). - First-party live feeds (
data-feeds): INSERTION-ORDER compact JSON — a frozen hash-compatibility surface that must never be re-sorted.
A payload signed under one form will not hash-match a re-derivation under the other, so
fetch_and_verify is form-aware: it tries the raw response bytes and every known form,
and if none reproduces the attested hash it raises CanonicalFormMismatchError —
deliberately NOT HashMismatchError, because a failed re-serialization cannot distinguish
tampering from a form mismatch. Fail closed either way: don't consume the payload; fetch
the exact delivered bytes and use byte-exact verify_payload.
Modules
ByteClient— low-level client holding the web3 contract instances (used byPublisher/Subscriber)Publisher— register a feed, publish data, sign EIP-712 PayloadAttestationsSubscriber— subscribe (register in DataRegistry + approve DataStream as a direct USDC spender), receive payloads, stream eventsGatewayClient— keyless x402 pay-per-call client (a wallet, not an API key)verify_payload/verify_event_payload— byte-exact payload verification against attestationsfetch_and_verify— archive fetch + form-aware verification (fails closed withCanonicalFormMismatchError)Mercat— feed search and discovery (connects to the indexer API)
Related
- ppb-sdk — the TypeScript sibling of this SDK
- byte-mcp-server — MCP server for AI agent integration
- byte-x402-gateway — keyless x402 payment gateway (a wallet, not an API key)
- byte-discovery-api — agent discovery endpoint
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file payperbyte_sdk-0.2.1.tar.gz.
File metadata
- Download URL: payperbyte_sdk-0.2.1.tar.gz
- Upload date:
- Size: 40.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ad879d401bb9c4b83d7fd7245d031fde90c44c242ca09262143f56c7ceb99c5
|
|
| MD5 |
a1ddf6111aa43aa69dd526a8a5d8d0d9
|
|
| BLAKE2b-256 |
df75b1b79a4f945671df3b7c428e4201dde63f426dd49a99c5b0c27316272751
|
File details
Details for the file payperbyte_sdk-0.2.1-py3-none-any.whl.
File metadata
- Download URL: payperbyte_sdk-0.2.1-py3-none-any.whl
- Upload date:
- Size: 35.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98f8fc851fff47a03d457c6f4f8309397213a8f9fc53b2325572ea9f6adc19d0
|
|
| MD5 |
2f2692014e542ced5e438eea48b57405
|
|
| BLAKE2b-256 |
b3aefbfefe2cdb60e978f67d8d5baee67f4ff231bc5b27164be768ff56a5b1e1
|