Arkiv SDK for Python — Getting Started
1) Arkiv "Hello, World!"
Create your account
This allows you to interact with Arkiv Testnet. Your is safe to share. Never share your .
Safety: The account you generate here is for Arkiv Testnet/sandbox use only. Never use it on any Mainnet.
2) Voting Board
You’ve written your first entity to Arkiv—now let’s build something that feels more alive. Using the same test account and client, we’ll create a tiny Voting Board: a simple structure where people can open proposals, cast votes, and read results directly from the chain.
This part of the guide shows how a few small entities can already form a collaborative application—powered by Arkiv.
You can keep experimenting right here in the CodePlayground, or set up the SDK locally to continue from your own environment.
1. Proposal
A single entity that defines what is being decided and how long the voting stays open (expiresIn).
2. Votes
Multiple small entities that reference the proposal by its entityKey and store each voter’s choice.
3. Tally
A read query that fetches all votes linked to a proposal and counts them—the simplest form of an on-chain result.
Next up: we’ll create the proposal entity—the anchor for every vote that follows.
3) Open Proposal
Create the decision “room”: a proposal entity with a bounded time window (Expires In). This is where votes will attach—still using the very same client/account you verified.
- Goal: Write a proposal entity with an expiration window (Expires In).
- Why it matters: Gives your vote stream a clear scope and predictable cost.
- Success check: You get a proposal.entityKey (the proposal ID).
1proposal_key, _ = client.arkiv.create_entity(
2 payload = b"Proposal: Switch stand-up to 9:30?",
3 content_type = "text/plain",
4 attributes = {
5 "type": "proposal",
6 "status": "open",
7 "version": 1,
8 },
9 expires_in = client.arkiv.to_seconds(days=3),
10)
11
12print("Proposal key:", proposal_key)4) Cast Votes
Attach votes to the proposal. Each vote is its own entity linked by proposalKey and attributed to a voter address. Same client, same journey—now with multiple actors.
- Goal: Create votes with { type="vote", proposalKey, voter, choice }.
- Why it matters: Votes are small, auditable facts you can query later.
- Success check: Two vote keys print, both linked to your proposal.
1voter = client.eth.default_account
2
3yes_vote_key, _ = client.arkiv.create_entity(
4 payload=b"vote: yes",
5 content_type="text/plain",
6 attributes={
7 "type": "vote",
8 "proposalKey": proposal_key,
9 "voter": voter,
10 "choice": "yes",
11 "weight": 1,
12 },
13 expires_in = client.arkiv.to_seconds(days=3),
14)
15
16print("Vote cast:", yes_vote_key)5) Batch Votes
Add many votes in one go—useful for demos, fixtures, or cross-proposal actions. You’re still operating with the same client and proposal context.
- Goal: Create multiple vote entities in a single call.
- Success check: Receipt count matches the number you pushed.
1vote_prefix = str(client.eth.default_account)
2creates: list[CreateOp] = []
3
4for i in range(5):
5 creates.append(CreateOp(
6 payload=f"vote: yes #{i+1}".encode("utf-8"),
7 content_type="text/plain",
8 attributes={
9 "type": "vote",
10 "proposalKey": proposal_key,
11 "voter": f"{vote_prefix}-bot{i}",
12 "choice": "yes" if i == 2 else "no",
13 "weight": 1,
14 },
15 expires_in = client.arkiv.to_seconds(days=3),
16 ))
17
18batch_receipt = client.arkiv.execute(Operations(creates=creates))
19print(f"Batch created: {len(batch_receipt.creates)} votes; tx={batch_receipt.tx_hash}")6) Tally Votes
Read the chain back. Query annotated entities to compute the result. Because reads are deterministic, the same query yields the same answer.
- Goal: Query votes by proposalKey and choice.
- Success check: YES/NO counts match your inputs.
1query_yes = f'type = "vote" and proposalKey = "{proposal_key}" and choice = "yes"'
2query_no = f'type = "vote" and proposalKey = "{proposal_key}" and choice = "no"'
3
4yes_votes = list(client.arkiv.query_entities(query_yes))
5no_votes = list(client.arkiv.query_entities(query_no))
6
7print(f"Tallies - YES: {len(yes_votes)}, NO: {len(no_votes)}")7) Watch Live
Subscribe to creations and extensions in real time. No polling—just logs as the story unfolds. Keep the same client; it already knows where to listen.
- Goal: Subscribe to creation and extension events for votes (and proposals).
- Success check: Console logs “[Vote created] …” or “[Vote extended] …”.
1def on_created(event, tx_hash):
2 try:
3 ent = client.arkiv.get_entity(event.key)
4 if ent.attributes and ent.attributes.get("type") in ("vote", "proposal"):
5 if ent.attributes.get("type") == "vote":
6 print(f"[Created] key={event.key} vote={ent.attributes.get('choice')} tx={tx_hash}")
7 else:
8 print(f"[Created] key={event.key} proposal={ent.payload} tx={tx_hash}")
9 except Exception as e:
10 print("[Created] error:", e)
11
12def on_extended(event, tx_hash):
13 try:
14 print(f"[Extended] key={event.key} new_expiration_block={event.new_expiration_block} tx={tx_hash}")
15 except Exception as e:
16 print("[Extended] error:", e)
17
18filter_created = client.arkiv.watch_entity_created(on_created, from_block="latest", auto_start=True)
19filter_extended = client.arkiv.watch_entity_extended(on_extended, from_block="latest", auto_start=True)
20
21# When done watching:
22# client.arkiv.cleanup_filters()8) Extend Window
Need more time to decide? Extend the proposal’s Expires In. You’re updating the same entity you opened earlier—continuing the narrative of one decision from start to finish.
- Goal: Extend the proposal entity by N blocks.
- Success check: Console prints the new expiration block.
1seconds = 2000
2receipt = client.arkiv.extend_entity(proposal_key, extend_by=seconds)
3print("Proposal extension receipt:", receipt.extensions)Setup & Installation
If you want to run this outside the browser (CI, local venv, a service), set up the Python SDK in your own project. This section shows virtualenv/Poetry and a placeholder script so you can run the same Voting Board flow from your terminal.
Prerequisites
Python 3.11+ recommended. Use a virtual environment (venv or Poetry).
Python 3.11+
3.12+ also fine
Virtual Environment
venv or Poetry
Ethereum Wallet
With test ETH for your RPC
RPC Endpoint
HTTP + (optionally) WS
Installation
1# Using venv + pip
2python3 -m venv .venv
3source .venv/bin/activate # on Windows: .venv\Scripts\activate
4pip install arkiv-sdk --pre
5pip install testcontainers websockets dotenvEnvironment Configuration
1# .env
2PRIVATE_KEY=0x... # use the (TEST) private key generated above
3RPC_URL=https://your.rpc.endpoint/rpc # e.g. https://mendoza.hoodi.arkiv.network/rpc
4WS_URL=wss://your.rpc.endpoint/rpc/ws # e.g. wss://mendoza.hoodi.arkiv.network/rpc/wsTroubleshooting
Invalid sender: Your RPC may point to an unexpected network for your key. Verify your RPC URL is correct.
Insufficient funds: Get test ETH from the faucet; writes require gas.
No events seen? Ensure fromBlock is low enough and keep the process running to receive logs.