antd-py -- Python SDK for Autonomi
Python SDK for the antd daemon. Provides synchronous and asynchronous clients with both REST and gRPC transports.
Installation
# REST transport (recommended)
pip install antd[rest]
# gRPC transport
pip install antd[grpc]
# Both transports
pip install antd[all]
# From source (development)
pip install -e ".[all]"
Compatibility
This package talks to a running antd daemon; it does not join the network itself. Python 3.10+. Tested against antd 0.12.x; health() fields such as version/evm_network need antd 0.4.0 or newer. For a daemon-less client see the ant-sdk package (import ant_ffi). Not related to the Ant Design UI library.
Quick Start
from antd import AntdClient
client = AntdClient() # REST transport, localhost:8082
# Health check
status = client.health()
print(f"{status.network} -- healthy: {status.ok}")
# Store and retrieve data
result = client.data_put_public(b"Hello, Autonomi!")
print(f"Address: {result.address}, chunks: {result.chunks_stored}")
data = client.data_get_public(result.address)
print(data.decode()) # "Hello, Autonomi!"
Transports
from antd import AntdClient, AsyncAntdClient
# REST (default)
client = AntdClient(transport="rest", base_url="http://localhost:8082", timeout=30)
# gRPC (wallet operations and payment_mode are available via REST only)
client = AntdClient(transport="grpc", target="localhost:50051")
# Async REST
aclient = AsyncAntdClient(transport="rest")
status = await aclient.health()
await aclient.close()
API Reference
Factory Functions
| Function | Description |
|---|---|
AntdClient(transport="rest", **kwargs) |
Create a synchronous client |
AsyncAntdClient(transport="rest", **kwargs) |
Create an asynchronous client |
Client Methods
Health
| Method | Returns | Description |
|---|---|---|
health() |
HealthStatus |
Check daemon health — also surfaces antd version, EVM network, uptime, build commit, and payment contract addresses (antd ≥ 0.4.0) |
Data
| Method | Returns | Description |
|---|---|---|
data_put_public(data, payment_mode=...) |
DataPutPublicResult |
Store public data — DataMap is stored on-network |
data_get_public(address: str) |
bytes |
Retrieve public data by address |
data_put(data, payment_mode=...) |
DataPutResult |
Store private (encrypted) data — DataMap returned to caller (NOT stored on-network) |
data_get(data_map: str) |
bytes |
Retrieve private data using a caller-held DataMap |
data_cost(data, payment_mode=...) |
UploadCostEstimate |
Estimate storage cost — size, chunks, gas, payment mode |
Chunks
| Method | Returns | Description |
|---|---|---|
chunk_put(data: bytes) |
PutResult |
Store a raw chunk |
chunk_get(address: str) |
bytes |
Retrieve a chunk |
Files
| Method | Returns | Description |
|---|---|---|
file_put(path, payment_mode=...) |
FilePutResult |
Upload a file privately — DataMap returned to caller (NOT stored on-network) |
file_get(data_map, dest_path) |
None |
Download a private file using a caller-held DataMap |
file_put_public(path, payment_mode=...) |
FilePutPublicResult |
Upload a file publicly — DataMap is stored on-network |
file_get_public(address, dest_path) |
None |
Download a public file by address |
file_cost(path, is_public, payment_mode=...) |
UploadCostEstimate |
Estimate file cost — size, chunks, gas, payment mode |
External Signer
Two-phase upload — daemon prepares the payment intent, caller signs + submits the payForQuotes tx, daemon finalizes once the chain confirms. See examples/07_external_signer.py + docs/external-signer-flow.md. A finalize that stores only some chunks raises PartialUploadError — see Partial uploads for how to finish the upload without paying twice.
| Method | Returns | Description |
|---|---|---|
prepare_upload(path, visibility=None) |
PrepareUploadResult |
Prepare a file upload for external signing |
prepare_upload_public(path) |
PrepareUploadResult |
Convenience for prepare_upload(path, visibility="public") |
prepare_data_upload(data, visibility=None) |
PrepareUploadResult |
Prepare a data upload for external signing |
prepare_chunk_upload(data) |
PrepareChunkResult |
Prepare a single chunk for external-signer publish |
finalize_upload(upload_id, tx_hashes) |
FinalizeUploadResult |
Submit a prepared upload after external payment. data_map_address populated when prepare used visibility="public". Raises PartialUploadError on a partial store |
finalize_merkle_upload(upload_id, winner_pool_hash, store_data_map=False) |
FinalizeUploadResult |
Submit a prepared merkle-batch upload after selecting the winning pool. Raises PartialUploadError on a partial store |
finalize_chunk_upload(upload_id, tx_hashes) |
str |
Submit a prepared chunk after external payment; returns the chunk address |
Models
All models are frozen dataclasses (immutable).
| Model | Fields | Description |
|---|---|---|
HealthStatus |
ok, network, version, evm_network, uptime_seconds, build_commit, payment_token_address, payment_vault_address |
Health check result (diagnostic fields require antd ≥ 0.4.0) |
PutResult |
cost, address |
Result of chunk_put only |
DataPutResult |
data_map, chunks_stored, payment_mode_used |
Private data put — DataMap returned to caller |
DataPutPublicResult |
address, chunks_stored, payment_mode_used |
Public data put — DataMap stored on-network |
FilePutResult |
data_map, storage_cost_atto, gas_cost_wei, chunks_stored, payment_mode_used |
Private file put — DataMap returned to caller |
FilePutPublicResult |
address, storage_cost_atto, gas_cost_wei, chunks_stored, payment_mode_used |
Public file put — DataMap stored on-network |
UploadCostEstimate |
cost, file_size, chunk_count, estimated_gas_cost_wei, payment_mode |
Pre-upload cost breakdown |
Error Handling
All errors inherit from AntdError:
from antd import AntdClient, AntdError, NotFoundError, PaymentError
client = AntdClient()
try:
data = client.data_get_public("nonexistent_address")
except NotFoundError:
print("Data not found on the network")
except PaymentError:
print("Insufficient funds")
except AntdError as e:
print(f"Error ({e.status_code}): {e}")
| Exception | HTTP | gRPC | Description |
|---|---|---|---|
BadRequestError |
400 | INVALID_ARGUMENT |
Invalid request parameters |
PaymentError |
402 | FAILED_PRECONDITION |
Wallet/payment issue |
NotFoundError |
404 | NOT_FOUND |
Resource not found |
AlreadyExistsError |
409 | ALREADY_EXISTS |
Resource already exists |
ForkError |
409 | ABORTED (non-partial-upload) |
Version conflict |
TooLargeError |
413 | RESOURCE_EXHAUSTED |
Payload too large |
InternalError |
500 | INTERNAL |
Server error |
NetworkError |
502 | UNAVAILABLE |
Network unreachable |
PartialUploadError |
502 (code: "PARTIAL_UPLOAD") |
ABORTED (message starts Partial upload:) |
Finalize stored some chunks, not all — subclass of NetworkError; carries chunks_stored, chunks_failed, total_chunks, retryable |
Partial uploads
finalize_upload / finalize_merkle_upload can fail after the wallet has paid: some chunks store, others miss quorum after the daemon's own retries. That surfaces as PartialUploadError (a NetworkError subclass, so existing except NetworkError / except AntdError blocks still catch it). The on-chain payment persists and the stored chunks stay on the network. retryable and retention_known say how to finish (retryable implies retention_known):
retryable— the daemon kept the paid attempt (payment proofs + unstored chunks) under the sameupload_id. Call the same finalize method again with the same arguments (the sameupload_idand payment artefacts) to store the remainder against the same payment: no re-prepare, no second signature, no double payment. Bound the loop — a persistent failure raises again on every call, so cap the attempts and treat achunks_failedthat stops shrinking as stuck. The retained attempt expires with the daemon's pending-upload TTL (one hour). Sent by antd ≥ 0.14.0.retention_known and not retryable— the daemon confirmed it kept nothing (e.g. a merkle finalize whose signer deliberately left some sub-batches unpaid). Re-prepare the same content: already-stored chunks are skipped, so the retry pays only for the remainder.not retention_known— retention is unknown. The daemon may still hold the paid attempt: it records the resume handle before it returns the error. Stop automatic recovery, keep theupload_idand the original payment artefacts (tx_hashes/winner_pool_hash), and reconcile before re-preparing or paying again. Never pay again on this signal alone. Daemons older than 0.14.0 never sendretryable, so their REST partial uploads read as unknown.
Over gRPC a partial upload used to raise ForkError; it now raises PartialUploadError. The daemon sends ABORTED only for PARTIAL_UPLOAD, so code that caught ForkError around a finalize should catch PartialUploadError.
Over REST the counts and flags come from the structured error body. Over gRPC a partial upload is an ABORTED status whose message starts with the daemon's fixed Partial upload: prefix, and the counts and flags are parsed from that message. An ABORTED that does not start with the prefix (including one that only embeds it, e.g. upstream error: Partial upload: ...) is not a partial upload and raises ForkError as before. Full contract: docs/external-signer-flow.md §6.
Malformed input is read conservatively and never escapes as a raw ValueError / TypeError / OverflowError:
- REST. Each count must be a JSON integer from 0 to 2^64−1. A quoted number (
"1"), bool, float (includingInfinity), array, object, negative or larger value reads as0.retryableisTrueonly for the JSON literaltrue;"true",1and everything else read asFalse.retention_knownisTrueonly whenretryableis a JSON bool (trueorfalse); absent,nullor any other type reads as unknown. Only a stringcodeof exactly"PARTIAL_UPLOAD"selectsPartialUploadError. A body that is not a JSON object, or has any othercode, keeps the plain 502 →NetworkErrormapping, as does a body Python cannot decode at all. A non-stringerrormakes the raw response body the message. - gRPC. The message reads
Partial upload: S/T chunks stored, F failed after retries: <reason> (<hint>).retention_knownisTrueonly when the message starts with that counts pattern, all three counts fit in a u64, and it ends with one of the daemon's two hints. A(paid attempt retained...)hint setsretryable. A(stored chunks persist; re-prepare the same content...)hint means the daemon confirmed nothing was retained; daemons older than 0.14.0 write only this one. A pattern miss or an out-of-range count reads the counts as0with both flagsFalse. Readable counts with a missing, truncated or unrecognised hint keep the counts, but both flags stayFalse: retention is unknown, not "nothing retained".
import time
from antd import PartialUploadError
def finalize_with_retry(client, upload_id, tx_hashes, max_attempts=5):
last_failed = None
for attempt in range(1, max_attempts + 1):
try:
return client.finalize_upload(upload_id, tx_hashes) # every chunk stored
except PartialUploadError as e:
if not e.retryable:
# retention_known: the daemon kept nothing, so re-prepare.
# Otherwise retention is unknown: stop, keep upload_id and
# tx_hashes, and reconcile. Never pay again on this alone.
raise
stuck = last_failed is not None and e.chunks_failed >= last_failed
if attempt == max_attempts or stuck:
raise # paid attempt still retained under upload_id: retry the same finalize later
last_failed = e.chunks_failed
time.sleep(2 * attempt)
Examples
Run examples from the examples/ directory:
# Requires antd daemon running on local testnet
python examples/01_connect.py # Health check
python examples/02_data.py # Store/retrieve data
python examples/03_chunks.py # Raw chunks
python examples/04_files.py # File upload/download
python examples/06_private_data.py # Private data with data maps
python examples/07_external_signer.py # External-signer file + chunk upload
python examples/08_grpc.py # gRPC transport (requires antd[grpc])
python examples/08_grpc.py # gRPC transport (instead of REST)
Or use the dev CLI:
ant dev example data
ant dev example all
Release files for antd 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| antd-0.2.0.tar.gz | 43.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| antd-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 109.2 kB
Release files / antd-0.2.0.tar.gz
| Download URL | antd-0.2.0.tar.gz |
|---|---|
| Size | 43.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3c964d86f0f6467db246c198fe167d1a7eb808ad8bfa7957dab3235244de18d1
|
|
BLAKE2b-256 checksum How to use checksums |
741c193d470e1d6b793f5bb3fbef8f67df4b75c89f853157aa3c742fa5e41cfd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.
Transparency logRelease files / antd-0.2.0-py3-none-any.whl
| Download URL | antd-0.2.0-py3-none-any.whl |
|---|---|
| Size | 65.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
208dc3121a2694b498f232bbc04f3ee43040e58ee58ebfe243e10221597cbb92
|
|
BLAKE2b-256 checksum How to use checksums |
b6f8b7ee7ddbf71ffcb79b17dd9213d608045a5d8b32e7364dd8e8c8383348ba
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.
Transparency log