usdf
Client for the USDF metered inference gateway, and a LangChain chat model. No required dependencies: the standard library's urllib, or httpx when it is installed. Python 3.9+. Paying from a wallet uses eth-account; the LangChain model uses langchain-core.
1 USDF = $1 of compute, redeemable 1:1 for USDG at any time. Every request is held for its worst case before it runs, billed from the provider's reported usage at the published price sheet, and answered with a receipt.
pip install usdf
Run a prompt
from usdf import USDF
# Pay per request from a wallet, no account: an eth-account account (or a private key).
client = USDF(wallet=wallet)
result = client.run("deepseek-v3.2", "Hello")
print(result.text, result.receipt.paid.usdf, result.receipt.asset)
# Or charge a prepaid balance with an API key.
prepaid = USDF(api_key="sk_...")
r = prepaid.run("deepseek-v3.2", "Hello", max_tokens=256)
print(r.text, r.receipt.cost.usdf, "USDF", r.receipt.request_id)
run(model, prompt_or_messages, max_tokens=, system=, temperature=, provider=, on_text=) returns .text, .receipt (the gateway's receipt; the full body is receipt.raw), .id, .completion and, over x402, .payment (the decoded PAYMENT-RESPONSE). on_text streams the answer piece by piece.
Unset, api_key, base_url (the gateway origin, default https://api.usdf.fi), session_token, rpc_url, max_amount and pay_to come from USDF_API_KEY, USDF_BASE_URL, USDF_SESSION_TOKEN, USDF_RPC_URL, USDF_MAX_AMOUNT and USDF_PAY_TO. A wallet given explicitly is the payer; the environment's key is not read then.
Paying from a wallet (x402)
The gateway answers an unpaid request with 402 and two accepted payments for the same amount: USDF through Permit2 and USDG through an EIP-3009 authorization. Neither costs the payer gas. The client reads the wallet's USDF allowance to Permit2 first, through rpc_url (or USDF_RPC_URL; unset, the chain's public RPC https://rpc.mainnet.chain.robinhood.com), which must answer for chain 4663: when it covers the amount it pays in USDF, otherwise in USDG, which needs no approval. A first-time payer whose USDF is not approved therefore pays USDG rather than a USDF payment the gateway would refuse. asset="USDF" or "USDG" fixes the choice; approve=True signs and sends the one approval a USDF payer ever needs, approve(Permit2, max) on USDF, through the same RPC. The receipt carries paid (what was charged, the quote), usage_cost, and the payment's asset, method, payer and payment_tx.
The 402 comes from whatever server answers at the base URL, so the client signs nothing on its word alone. It pays only: on Robinhood Chain (eip155:4663); USDF (0x4A35a5eC2A50A4d2957B2F2366E017f526cAB318) through Permit2 or USDG (0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168) through EIP-3009 under USDG's own domain (Global Dollar, version 1); to the payee the gateway names: its own 402 when the base URL is the gateway (https://api.usdf.fi), else the address the gateway's GET https://api.usdf.fi/v1/config publishes (x402.pay_to), else the gateway's hot wallet (0x13009Bd7116f8C955f92409467743ee67AB1990b); pay_to (or USDF_PAY_TO, comma-separated) pins another payee, and then only that one; with a validity window of at most an hour; and at most max_amount base units per payment (USDF_MAX_AMOUNT; default 1000000, 1 USDF). Anything else raises USDFError with type payment_refused and a code (untrusted_network, untrusted_asset, untrusted_domain, untrusted_payee, amount_over_limit, untrusted_amount, untrusted_timeout, wrong_chain) before any approval, read or signature. The exact scheme charges the quote, the request's worst case at its max_tokens, so a lower max_tokens is the way to a smaller payment.
from eth_account import Account
from usdf import USDF
client = USDF(wallet=Account.from_key(key), rpc_url="https://rpc.mainnet.chain.robinhood.com", approve=True)
sign_exact_payment (which applies the same checks), check_accept, payment_limits, choose_accept, wallet_signer, read_permit2_allowance, approve_permit2, the header codecs and the constants (CHAIN_ID, USDF_ADDRESS, USDG_ADDRESS, GATEWAY_PAYEE, PUBLIC_RPC_URL, DEFAULT_MAX_AMOUNT) are exported for other signers.
LangChain
from usdf.langchain import USDFChat
llm = USDFChat(model="deepseek-v3.2", wallet=wallet) # or api_key="sk_..."
reply = llm.invoke("Hello")
print(reply.content, reply.response_metadata["receipt"]["request_id"], reply.usage_metadata)
pip install usdf langchain (or usdf[langchain] for langchain-core alone). stream() yields the answer as it arrives and ends with a chunk carrying the receipt. The settings are those of USDF: api_key, wallet, base_url, asset, approve, rpc_url, max_amount, pay_to, plus max_tokens, temperature and provider (privacy and routing asks). api_key is a secret (masked, left out of model_dump, named in lc_secrets) and wallet is left out of the model's repr and of the serialized form LangChain hands to callbacks and tracers.
Chat completions
client = USDF(api_key="sk_...")
c = client.chat.completions.create("deepseek-v3.2", [{"role": "user", "content": "Hello"}], max_tokens=256)
print(c.content, c.receipt.cost.usdf)
s = client.chat.completions.create("deepseek-v3.2", messages, max_tokens=256, stream=True)
for chunk in s:
print((chunk["choices"][0]["delta"].get("content") if chunk["choices"] else "") or "", end="")
print(s.receipt.cost.usdf if s.receipt else "stopped early")
Keyword arguments are sent as given (max_tokens, temperature, tools, provider, ...). A stream exposes request_id from the first byte and receipt once read to the end; interrupted is set when the provider cut the answer (billed, receipt partial); a stream that produced nothing raises USDFError upstream_unavailable and nothing is charged. A request served on attested hardware carries receipt.attestation; client.receipts.attestation(id) returns the provider's signed receipt.
Batch jobs
job = client.batches.create(batch_model, messages, max_tokens=256) # a model whose price carries batch_input
done = client.batches.wait(job["id"]) # the completion; done.receipt.batch is True
Before and after a call
quote(model, prompt=, max_tokens=) (no key), balance(), receipts.get(id) (with log, the request's leaf in the usage log), receipts.list(limit=, model=, status=) (a key's own, or with only a session the account's), models.list(), pricing(), embeddings.create(model, input). usdf.estimate reproduces the gateway's hold arithmetic.
The account (session)
Everything a key can do (requests, quotes, the balance, receipts, tools) needs only the key: create one on the dashboard at usdf.fi. The account's own calls take a session: sign in at usdf.fi and pass its session token, USDF(session_token=...) or USDF_SESSION_TOKEN (it lasts about an hour). There is no email and password sign-in. client.auth.info() reads GET /v1/auth (the sign-in in force and every account path); client.auth.sign_out() ends the session at the gateway.
keys.create(label=, budget_units=, rate_limit_rpm=),keys.list(revoked=),keys.revoke(id),keys.rotate(id)(a new key with the old one's controls; the old one is revoked),keys.limits(id),keys.update(id, spend_limit_hour_units=, spend_limit_day_units=, spend_limit_month_units=, allowed_models=, paused=, no_logs=, private_tier=, webhook_url=),keys.pause(id),keys.resume(id).account.get(),account.overview(),account.activity(model=, status=, from_=, to=, limit=),account.activity_csv(...),account.settings(),account.update_settings(...),account.analytics(days=),account.withdraw(units, chain_id=)(to the linked wallet).links.create(units, memo=, expires_in_days=)(the token is returned once),links.list(),links.get(token),links.claim(token),links.cancel(link_id).agents.create(slug, name, **policy),agents.list(),agents.get(slug),agents.update(slug, **policy),agents.fund(slug, units, direction=, idempotency_key=),agents.pay(slug, to, units, memo=, idempotency_key=),agents.payments(slug),agents.rotate_key(slug).zap.quote(token, amount, recipient=, slippage_bps=): ETH or a token with a liquidity pool against USDG, into USDF, with the transactions to send. No key.pool.read(): the USDF/USDG pool's price, tick, whether it sits in the seeded band, its liquidity and reserves. No key.
Money: base units (1 unit = $0.000001 = 0.000001 USDF); amounts are Amount(units, usd, usdf) or {units, usd, usdf}.
Errors
Every refusal raises USDFError with the gateway's status, type, code, message, request_id and retry_after. Nothing is charged for a refusal.
Changes
0.1.2. A wallet payment goes to the payee the gateway names, not to a hot wallet fixed in the package: the gateway rotated its custody hot wallet on 2026-10-26, and 0.1.1, which pays only the retired one (0x52Fe...9F05), refuses the new one's 402 with untrusted_payee before signing anything. The 402's payee is taken as it stands from https://api.usdf.fi; through any other base URL it must be the one https://api.usdf.fi/v1/config publishes, or the new hot wallet (GATEWAY_PAYEE). pay_to and USDF_PAY_TO still pin a payee.
0.1.1. The default base URL is https://api.usdf.fi, the gateway's own domain; 0.1.0 used the hosting provider's hostname for the same gateway, which still answers. base_url or USDF_BASE_URL still points the client elsewhere. No other change.
Metadata
Release files for usdf 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| usdf-0.1.2.tar.gz | 37.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| usdf-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 79.7 kB
Release files / usdf-0.1.2.tar.gz
| Download URL | usdf-0.1.2.tar.gz |
|---|---|
| Size | 37.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
03f3192913cbc694656d5fbd881bbfff50e9502e806252557c7872e9b456dcfe
|
|
BLAKE2b-256 checksum How to use checksums |
4864f9924a2396c89edf2417ed0f4422cd27f788a1007b5bef8743fe7ddb3be1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / usdf-0.1.2-py3-none-any.whl
| Download URL | usdf-0.1.2-py3-none-any.whl |
|---|---|
| Size | 42.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
19abca286bac6e2f74cf4d9cf4dc62355ab01f6632afc487d5b78d9011977662
|
|
BLAKE2b-256 checksum How to use checksums |
18b9ea31120ad86ede093459757cf1ba47fe538397fd6805cef43d21d88c13db
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Security for usdf 0.1.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.