0G Storage — Python SDK
Python SDK for the 0G Storage Network — a decentralized storage layer that spreads files across sharded nodes and anchors integrity with on-chain merkle proofs.
A direct port of the official TypeScript SDK @0glabs/0g-ts-sdk. Files uploaded with either SDK produce byte-exact identical merkle roots.
📚 Full developer documentation: og-py.vercel.app
What you get
- Merkle tree generation — Keccak256, 256-byte chunks, 256 KB segments — output identical to the TS SDK
- File upload — Flow contract submission, parallel segment uploads, automatic shard routing, retry on transient failures
- File download — by root hash, with optional proof verification per segment
- Splitable upload — for files larger than 4 GB, with batched fragment processing
- Fragment download — reassemble multi-fragment files in order
- KV storage — append-only key-value streams with version-aware reads, batched writes, and per-key access control
- Storage node RPC — 14-method client for direct node interaction (get_status, upload/download segments, shard configs, sector proofs)
Installation
pip install 0g-storage-sdk
Requires Python 3.8+.
The package name 0g-storage-sdk isn't a valid Python identifier, so the SDK ships its top-level modules directly:
from core.indexer import Indexer
from core.file import ZgFile
from core.merkle import MerkleTree
from core.kv import KvClient, StreamDataBuilder, Batcher
Dependencies
Auto-installed:
pycryptodome— Keccak256 hashingweb3— blockchain RPC and contract callseth-account— transaction signingrequests— HTTP client
Quickstart — upload & download
from core.indexer import Indexer
from core.file import ZgFile
from eth_account import Account
INDEXER_RPC = "https://indexer-storage-testnet-turbo.0g.ai"
BLOCKCHAIN_RPC = "https://evmrpc-testnet.0g.ai"
PRIVATE_KEY = "0xYOUR_PRIVATE_KEY"
indexer = Indexer(INDEXER_RPC)
account = Account.from_key(PRIVATE_KEY)
# Upload
file = ZgFile.from_file_path("./data.txt")
result, err = indexer.upload(
file,
BLOCKCHAIN_RPC,
account,
{
"tags": b"\x00",
"finalityRequired": True,
"expectedReplica": 1,
"account": account,
},
)
file.close()
if err is None:
print(f"Uploaded: {result['rootHash']}")
print(f"Tx: {result['txHash']}")
# Download (wait 3–5 minutes for shard propagation first)
err = indexer.download(result["rootHash"], "./output.txt")
Client-side encryption
Files can be encrypted before upload using AES-256-CTR (v1) or ECIES on secp256k1 (v2). Both schemes are wire-compatible with the TS SDK and the Go storage client.
import os
from core.indexer import Indexer
indexer = Indexer(INDEXER_URL)
file = ZgFile.from_file_path("./secret.txt")
# v1 — symmetric (you generate and manage a 32-byte key out-of-band)
key = os.urandom(32)
result, err = indexer.upload(file, BLOCKCHAIN_RPC, signer, upload_opts={
"encryption": {"type": "aes256", "key": key},
})
# Download + auto-decrypt (best-effort; raw bytes on disk if no match)
indexer.download(result["rootHash"], "./secret.txt", symmetric_key=key)
# v2 — ECIES (anyone with `recipient_pub` can encrypt; only the holder of
# the matching private key can decrypt)
recipient_pub = "0x031b84c5...078f" # 33-byte compressed secp256k1 pubkey
result, err = indexer.upload(file, BLOCKCHAIN_RPC, signer, upload_opts={
"encryption": {"type": "ecies", "recipient_pub_key": recipient_pub},
})
indexer.download(result["rootHash"], "./secret.txt", private_key=PRIVATE_KEY_HEX)
To detect whether a stored file is encrypted before downloading:
header, err = indexer.peek_header(root_hash)
if header is None:
print("Plaintext file")
elif header.version == 1:
print("AES-256 — supply symmetric_key=...")
elif header.version == 2:
print("ECIES — supply private_key=...")
Compute a merkle root locally
No network calls — pure local hashing:
from core.file import ZgFile
file = ZgFile.from_bytes(b"hello, 0G Storage")
tree, err = file.merkle_tree()
print(tree.root_hash())
file.close()
Large files (>4 GB)
from core.uploader import Uploader
# After selecting nodes via indexer.select_nodes(...)
uploader = Uploader(clients, BLOCKCHAIN_RPC, flow)
result, err = uploader.splitable_upload(
file,
{
"account": account,
"fragmentSize": 4 * 1024 * 1024 * 1024, # 4 GB per fragment
"finalityRequired": True,
},
)
# result["rootHashes"] is a list — one hash per fragment, in order
Download with Downloader.download_fragments(roots, filename) — fragments are concatenated in the order you pass them.
KV storage
from core.kv import Batcher
batcher = Batcher(version=1, clients=clients, flow=flow, provider=BLOCKCHAIN_RPC)
stream_id = "0x" + "11" * 32
batcher.set(stream_id, b"user:42", b'{"name": "Alice"}')
batcher.set(stream_id, b"user:101", b'{"name": "Bob"}')
result, err = batcher.exec({"account": account})
Read back via KvClient:
from core.kv import KvClient
kv = KvClient("http://storage-node.example.com:5678")
value = kv.get_value(stream_id, b"user:42")
# value.data is base64-encoded
Generate proofs
proof = tree.proof_at(2) # proof for the leaf at index 2
err = proof.validate(
root_hash = tree.root_hash(),
content = b"chunk 3 data",
position = 2,
num_leaf_nodes = len(tree.leaves),
)
if err is None:
print("Valid proof")
Public API surface
Everything you need is exported at module level:
| Symbol | Purpose |
|---|---|
Indexer(url) |
Indexer RPC client — upload, download, select_nodes, get_file_locations |
ZgFile.from_file_path(path) / .from_bytes(data) |
File abstraction with merkle tree generation |
MerkleTree, Proof, LeafNode |
Direct merkle tree construction and verification |
Uploader, Downloader |
Lower-level upload/download with retry options |
StorageNode(url) |
Direct RPC to a storage node (14 methods) |
KvClient(rpc) |
KV reads — get_value, get_first/last/next/prev, iterators |
StreamDataBuilder(version) |
Build StreamData blobs (writes + access control) |
Batcher(version, clients, flow, provider) |
High-level batched KV writes |
KvIterator |
Cursor-style traversal over a stream |
select_nodes, check_replica, is_valid_config |
Node selection helpers |
RetryOpts, tx_with_gas_adjustment, submit_with_gas_adjustment |
Retry and gas utilities |
Exceptions: StorageError, UploadError, DownloadError, MerkleTreeError, ContractError, RetryableError, ... |
Structured exception hierarchy with error_code, context, cause |
For the full per-class API and worked examples, see the Storage SDK docs.
Network configuration
| Network | Chain ID | Blockchain RPC | Indexer RPC |
|---|---|---|---|
| Mainnet | 16661 |
https://evmrpc.0g.ai |
https://indexer-storage-turbo.0g.ai |
| Testnet (Galileo) | 16602 |
https://evmrpc-testnet.0g.ai |
https://indexer-storage-testnet-turbo.0g.ai |
Get testnet tokens at faucet.0g.ai.
Contract addresses
The SDK reads the Flow contract address from each storage node's networkIdentity automatically — direct addressing is rarely needed.
| Contract | Testnet | Mainnet |
|---|---|---|
| Flow | 0x22E03a6A89B950F1c82ec5e74F8eCa321a105296 |
0x62D4144dB0F0a6fBBaeb6296c785C71B3D57C526 |
Constants
DEFAULT_CHUNK_SIZE = 256 # bytes per merkle leaf
DEFAULT_SEGMENT_SIZE = 262144 # 256 KB per segment (1024 chunks)
SMALL_FILE_SIZE_THRESHOLD = 256 * 1024
DEFAULT_FRAGMENT_SIZE = 4 * 1024 * 1024 * 1024 # 4 GB
Testing
cd 0g_py_storage
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
pytest tests/ -v
Verifying parity with the TypeScript SDK
from core.file import ZgFile
tree, _ = ZgFile.from_bytes(b"0G Storage parity check").merkle_tree()
print(tree.root_hash())
Run the equivalent in the TypeScript SDK — the printed hashes match byte-for-byte.
Error handling
from utils.error_handler import is_retryable
result, err = indexer.upload(file, BLOCKCHAIN_RPC, account, opts)
if err is not None:
if is_retryable(err):
# transient — retry with backoff
...
else:
raise err
For a tour of the full exception hierarchy and ErrorContext / wrap_with_context helpers, see the Error Handling docs.
Contributing
Contributions should preserve TypeScript SDK parity. Each PR should:
- Match the TS SDK behavior — verify outputs match byte-for-byte
- Include tests for new functionality
- Update the README and docs as needed
- Reference TS SDK line numbers in code comments where applicable
License
MIT
Links
- 📚 Documentation: og-py.vercel.app
- 🌐 0G Labs: 0g.ai
- 📖 Official docs: docs.0g.ai
- 📦 TypeScript SDK: @0glabs/0g-ts-sdk
- 🚰 Testnet faucet: faucet.0g.ai
- 🔍 Block explorer: chainscan-galileo.0g.ai
Metadata
Release files for 0g-storage-sdk 0.4.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 | Provenance | Actions |
|---|---|---|---|---|
| 0g_storage_sdk-0.4.0.tar.gz | 75.8 kB | Without provenance | Details for 0g_storage_sdk-0.4.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 0g_storage_sdk-0.4.0-py3-none-any.whl | Python 3 | none | any | Details for 0g_storage_sdk-0.4.0-py3-none-any.whl |
Total release size: 150.0 kB
Release files / 0g_storage_sdk-0.4.0.tar.gz
| Download URL | 0g_storage_sdk-0.4.0.tar.gz |
|---|---|
| Size | 75.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c3b73eed383d6439af037683431f637b9c5d15c45ae0d9627d3189b8b124907b
|
|
BLAKE2b-256 checksum How to use checksums |
646456834c6c27452410cb5f275ab33ef6153c1c5b6e47fdcbc14230ca06e73f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|
Release files / 0g_storage_sdk-0.4.0-py3-none-any.whl
| Download URL | 0g_storage_sdk-0.4.0-py3-none-any.whl |
|---|---|
| Size | 74.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
342e753b221d7dddf3f1856d7f8240a4f4d8a34d7e44e0fb3b4fa7158abef416
|
|
BLAKE2b-256 checksum How to use checksums |
b02f237a02b0c8aea83beeb9611024be823c2c9fd33c638410f4a25f5552063a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|
Security for 0g-storage-sdk 0.4.0
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.