> Feedback: If these docs are stale, missing, or confusing, post sanitized feedback to `https://tempo.xyz/developers/api/feedback` with `source: "mcp"`, a short `message`, and any relevant `toolName`, `relatedResource`, or `client`.
> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://tempo.xyz/developers/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# Python SDK

Tempo distributes a Python SDK as a [web3.py](https://web3py.readthedocs.io/) extension. The SDK adds native support for Tempo Transactions, including call batching, fee sponsorship, and access key management.

The Tempo Python SDK can be used to perform common operations with the chain, such as: sending Tempo Transactions, batching multiple calls, fee sponsorship, and more.

::::steps
## Install the Python SDK

To install the Tempo Python SDK:

```bash [pip]
pip install pytempo
```

:::tip
The SDK requires Python 3.9 or higher and web3.py 7.0+.
:::

## Create a Python client

To interact with Tempo, create a web3.py client connected to a Tempo node:

```python [main.py]
from web3 import Web3

w3 = Web3(Web3.HTTPProvider("https://rpc.tempo.xyz")) # [!code hl]

block_number = w3.eth.block_number
print(f"Connected to Tempo at block {block_number}")
```

## Send a Tempo transaction with Python

Build and send a transaction using the `TempoTransaction` class:

```python [main.py]
import os
from web3 import Web3
from pytempo import Call, TempoTransaction # [!code hl]

w3 = Web3(Web3.HTTPProvider("https://rpc.tempo.xyz"))
private_key = os.environ["PRIVATE_KEY"]
account = w3.eth.account.from_key(private_key)

# [!code hl:12]
tx = TempoTransaction.create(
    chain_id=w3.eth.chain_id,
    gas_limit=100_000,
    max_fee_per_gas=w3.eth.gas_price * 2,
    max_priority_fee_per_gas=w3.eth.gas_price,
    nonce=w3.eth.get_transaction_count(account.address),
    calls=(
        Call.create(to="0x70997970C51812dc3A010C7d01b50e0d17dc79C8"),
    ),
)

signed_tx = tx.sign(private_key) # [!code hl]
tx_hash = w3.eth.send_raw_transaction(signed_tx.encode()) # [!code hl]
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Transaction hash: {tx_hash.hex()}")
```
::::

## Python SDK examples

### Send a token transfer with Python

Send a TIP-20 token transfer using pytempo's typed contract helpers:

```python [transfer.py]
from pytempo import TempoTransaction
from pytempo.contracts import TIP20, ALPHA_USD

tx = TempoTransaction.create(
    chain_id=4217,
    gas_limit=100_000,
    max_fee_per_gas=10_000_000_000,
    max_priority_fee_per_gas=1_000_000_000,
    nonce=w3.eth.get_transaction_count(account.address),
    calls=(
        TIP20(ALPHA_USD).transfer( # [!code hl]
            to="0x70997970C51812dc3A010C7d01b50e0d17dc79C8", # [!code hl]
            amount=100_000_000, # 100 tokens (6 decimals) # [!code hl]
        ), # [!code hl]
    ),
)
```

### Pay fees in a stablecoin with Python

Use a TIP-20 token to pay for transaction fees instead of the native token:

```python [fee_token.py]
from pytempo import TempoTransaction, Call
from pytempo.contracts import ALPHA_USD

tx = TempoTransaction.create(
    chain_id=4217,
    gas_limit=100_000,
    max_fee_per_gas=10_000_000_000,
    max_priority_fee_per_gas=1_000_000_000,
    nonce=w3.eth.get_transaction_count(account.address),
    fee_token=ALPHA_USD, # [!code hl]
    calls=(
        Call.create(to="0x70997970C51812dc3A010C7d01b50e0d17dc79C8"),
    ),
)
```

### Batch multiple calls with Python

Execute multiple operations atomically in a single transaction:

```python [batch.py]
from pytempo import TempoTransaction
from pytempo.contracts import TIP20, ALPHA_USD

token = TIP20(ALPHA_USD)

tx = TempoTransaction.create(
    chain_id=4217,
    gas_limit=300_000,
    max_fee_per_gas=10_000_000_000,
    max_priority_fee_per_gas=1_000_000_000,
    nonce=w3.eth.get_transaction_count(account.address),
    calls=(
        token.transfer(to="0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbb", amount=100_000_000), # [!code hl]
        token.transfer(to="0x70997970C51812dc3A010C7d01b50e0d17dc79C8", amount=50_000_000), # [!code hl]
        token.transfer(to="0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", amount=25_000_000), # [!code hl]
    ),
)

signed_tx = tx.sign(private_key)
tx_hash = w3.eth.send_raw_transaction(signed_tx.encode())
```

### Parallel Transactions (2D Nonces)

Send multiple transactions concurrently using different nonce keys:

```python [parallel.py]
tx1 = TempoTransaction.create(
    chain_id=4217,
    gas_limit=100_000,
    max_fee_per_gas=10_000_000_000,
    max_priority_fee_per_gas=1_000_000_000,
    nonce_key=1, # Sequence A // [!code hl]
    nonce=0,
    calls=(Call.create(to=recipient1, data=data1),),
)

tx2 = TempoTransaction.create(
    chain_id=4217,
    gas_limit=100_000,
    max_fee_per_gas=10_000_000_000,
    max_priority_fee_per_gas=1_000_000_000,
    nonce_key=2, # Sequence B (parallel) // [!code hl]
    nonce=0,
    calls=(Call.create(to=recipient2, data=data2),),
)

# Sign and send both in parallel
signed_tx1 = tx1.sign(private_key)
signed_tx2 = tx2.sign(private_key)
w3.eth.send_raw_transaction(signed_tx1.encode())
w3.eth.send_raw_transaction(signed_tx2.encode())
```

### Fee sponsorship with Python

Have another account pay for transaction fees:

```python [fee_payer.py]
# User creates and signs a transaction marked for fee sponsorship
tx = TempoTransaction.create(
    chain_id=4217,
    gas_limit=100_000,
    max_fee_per_gas=10_000_000_000,
    max_priority_fee_per_gas=1_000_000_000,
    awaiting_fee_payer=True, # [!code hl]
    calls=(Call.create(to=recipient, data=data),),
)

signed_by_user = tx.sign(user_private_key)
final_tx = signed_by_user.sign(fee_payer_private_key, for_fee_payer=True) # [!code hl]
w3.eth.send_raw_transaction(final_tx.encode())
```

### Transaction validity windows in Python

Set a time window during which the transaction is valid:

```python [validity.py]
import time

now = int(time.time())

tx = TempoTransaction.create(
    chain_id=4217,
    gas_limit=100_000,
    max_fee_per_gas=10_000_000_000,
    max_priority_fee_per_gas=1_000_000_000,
    nonce=nonce,
    valid_after=now, # [!code hl]
    valid_before=now + 3600, # 1 hour from now // [!code hl]
    calls=(Call.create(to=recipient, data=data),),
)
```

## Account Keychain in the Python SDK

The `AccountKeychain` class provides typed helpers for Tempo's [Account Keychain precompile](/docs/protocol/transactions/AccountKeychain), enabling access key management directly from Python.

:::info
Enhanced access key features — periodic spending limits and call scoping — require the [T3 network upgrade](/docs/protocol/upgrades/t3).
:::

```python [keychain.py]
from pytempo import (
    TempoTransaction, Call,
    KeyRestrictions, SignatureType, TokenLimit, CallScope,
)
from pytempo.contracts import AccountKeychain, ALPHA_USD
from web3 import Web3

w3 = Web3(Web3.HTTPProvider("https://rpc.tempo.xyz"))

# Authorize a new access key (secp256k1, no expiry):
call = AccountKeychain.authorize_key(
    key_id="<ACCESS_KEY_ADDRESS>",
    signature_type=SignatureType.SECP256K1,
    restrictions=KeyRestrictions(expiry=0),
)
tx = TempoTransaction.create(
    chain_id=w3.eth.chain_id,
    gas_limit=200_000,
    max_fee_per_gas=w3.eth.gas_price * 2,
    max_priority_fee_per_gas=w3.eth.gas_price,
    nonce=w3.eth.get_transaction_count(account.address),
    calls=(call,),
)

# Authorize with a spending limit:
call = AccountKeychain.authorize_key(
    key_id="<ACCESS_KEY_ADDRESS>",
    signature_type=SignatureType.SECP256K1,
    restrictions=KeyRestrictions(
        expiry=0,
        limits=[TokenLimit(token=ALPHA_USD, limit=1_000_000)],
    ),
)

# Authorize with call scopes (restrict to specific contracts/functions):
call = AccountKeychain.authorize_key(
    key_id="<ACCESS_KEY_ADDRESS>",
    signature_type=SignatureType.SECP256K1,
    restrictions=KeyRestrictions(
        expiry=0,
        allowed_calls=[
            CallScope.transfer(target=ALPHA_USD),
            CallScope.approve(target=ALPHA_USD),
        ],
    ),
)

# Full example: 24h expiry + spending limit + call scope:
import time
expiry = int(time.time()) + 86400
call = AccountKeychain.authorize_key(
    key_id="<ACCESS_KEY_ADDRESS>",
    signature_type=SignatureType.SECP256K1,
    restrictions=KeyRestrictions(
        expiry=expiry,
        limits=[TokenLimit(token=ALPHA_USD, limit=1_000_000)],
        allowed_calls=[CallScope.transfer(target=ALPHA_USD)],
    ),
)

# Revoke an access key (permanent, cannot be re-authorized):
call = AccountKeychain.revoke_key(key_id="<ACCESS_KEY_ADDRESS>")

# Update spending limit for a key-token pair:
call = AccountKeychain.update_spending_limit(
    key_id="<ACCESS_KEY_ADDRESS>",
    token=str(ALPHA_USD),
    new_limit=2_000_000,
)

# Replace all call scopes for a key:
call = AccountKeychain.set_allowed_calls(
    key_id="<ACCESS_KEY_ADDRESS>",
    scopes=[
        CallScope.transfer(target=ALPHA_USD),
        CallScope.unrestricted(target="<CONTRACT_ADDRESS>"),
    ],
)

# Remove a target contract from allowed call list:
call = AccountKeychain.remove_allowed_calls(
    key_id="<ACCESS_KEY_ADDRESS>",
    target="<TARGET_ADDRESS>",
)

# Query key info (read-only):
key_info = AccountKeychain.get_key(
    w3,
    account_address="<ACCOUNT_ADDRESS>",
    key_id="<ACCESS_KEY_ADDRESS>",
)
print(key_info)
# {'signature_type': 0, 'key_id': '0x...', 'expiry': 1893456000, ...}

# Query remaining spending limit:
remaining = AccountKeychain.get_remaining_limit(
    w3,
    account_address="<ACCOUNT_ADDRESS>",
    key_id="<ACCESS_KEY_ADDRESS>",
    token_address=str(ALPHA_USD),
)
print(f"Remaining: {remaining}")
```

### Sign with an access key in Python

Use `sign_access_key` to sign a transaction as an access key holder:

```python [access_key_sign.py]
from pytempo import TempoTransaction, Call

tx = TempoTransaction.create(
    chain_id=w3.eth.chain_id,
    gas_limit=100_000,
    max_fee_per_gas=w3.eth.gas_price * 2,
    max_priority_fee_per_gas=w3.eth.gas_price,
    nonce=w3.eth.get_transaction_count(root_account_address),
    calls=(Call.create(to="<CONTRACT_ADDRESS>"),),
)

signed_tx = tx.sign_access_key( # [!code hl]
    access_key_private_key="<ACCESS_KEY_PRIVATE_KEY>", # [!code hl]
    root_account="<ROOT_ACCOUNT_ADDRESS>", # [!code hl]
) # [!code hl]
tx_hash = w3.eth.send_raw_transaction(signed_tx.encode())
```

## Next steps for the Python SDK

After setting up the Python SDK, you can:

* Follow a guide on how to [make payments](/docs/guide/payments), [issue stablecoins](/docs/guide/issuance), [exchange stablecoins](/docs/guide/stablecoin-dex), and [more](/docs).
* View the [source on GitHub](https://github.com/tempoxyz/pytempo)
* View the [package on PyPI](https://pypi.org/project/pytempo/)
