> Feedback: If these docs are stale, missing, or confusing, post sanitized feedback to `https://docs.tempo.xyz/api/feedback` with `source: "mcp"`, a short `message`, and any relevant `toolName`, `relatedResource`, or `client`.
# Python

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

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 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 Transaction

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()}")
```

::::

## Examples

### Token Transfer

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

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

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

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 Window

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

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}")
```

### Signing with an Access Key

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

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/)
