Files
status-python-sdk/docs/account.md
T

57 KiB
Raw Blame History

Account

Account header image

The account class allows you to easily work with a Status account.

Display name

The display name is the humanreadable identifier for a Status account. It is used when creating an account, resolving an existing account during login, and when updating the account name through the display_name property.

Display names must follow strict validation rules enforced by the library and expected by the Status application. A valid display name must satisfy all of the following conditions:

  • It may contain uppercase letters (AZ)
  • It may contain spaces ( )
  • It may contain numbers (09)
  • It may contain hyphens (-)
  • It may contain underscores (_)
  • It must be at least 5 characters long
  • It cannot be more than 24 characters long
  • It cannot start or end with a space

Characters such as spaces, punctuation, emojis, or other symbols are not allowed.

Valid examples

alpha_01
STATUS-01
bot_user_5
HELLO123
node-42

Invalid examples

Example Reason
bot Too short (minimum length is 5)
mybot Leading space
mybot Trailing space
bot!123 Contains invalid character !

If a display name does not follow these rules, a ValueError will be raised by the account validation logic.

Backups

Backup files (.bkp) can be both created in Status App and the Python SDK.

Status App Backup

Status Backend backup folder is exposed in a Docker volume so users can:

  • Upload backup - by dropping .bkp files in the backups folder locally (linked to Status Backend Docker container). Backups are automatically uploaded if a mnemonic is provided during login.
  • Create backup - by using backup() or creating one in Status App.

Note: Status App will not automatically backup messages. This has to be manually overridden on the app (above screenshot). When using the Python SDK, the messages are automatically stored in the .bkp files.

You can point backup_folder at the folder Status App uses for its own local backups. Because both Status App and the SDK name .bkp files deterministically from the account's compressed key (see backup()), a backup created by the SDK lands with the exact filename Status App expects, and Status App will pick it up and load it directly - and likewise, a backup created in Status App can be auto-loaded by the SDK during recovery. This lets you move backups between the app and the SDK without renaming anything.

flowchart LR

    app[Status App]
    sdk[Python SDK]
    subgraph shared["Shared folder"]
        bkp1[.bkp 1]
        bkp2[.bkp 2]
        bkpn[.bkp n]
    end
    
    subgraph backend[status-im/status-go]
       Vol1[(Backup)]
    end

    app <--> shared <--> sdk <--> Vol1

Because the filename is derived from the account's key rather than from whoever wrote it, the same account always maps to the same .bkp file - so neither side needs to know which tool produced the backup.

Wallet

Wallet features are optional and can be omitted if not required for your use case. They provide functionality equivalent to the Wallet and Market tabs.

Status App Wallet

Account(domain="localhost", backend_port=8080, media_port=9000, is_secure=False, backup_folder=None)

Create a new Account instance ready to be logged in. The constructor wires the SDK to a running Status Backend at the given domain and backend_port, prepares the local assets/ folder (used for image uploads, such as the profile picture) and backups/ folder (used for backup uploads and recovery).

Name Type Required Description
domain str No Domain where Status Backend is reachable. Defaults to localhost when running through launch_docker_container on the same machine. Use the container name when the SDK runs inside the same Docker network as Status Backend.
backend_port int No Port exposed by Status Backend. Defaults to 8080. If this is changed, the published port for backend_port must be updated to match in docker-compose.yaml as well.
media_port int No Port exposed by the Status media server, used to fetch localhost images such as the profile picture. Defaults to 9000. If this is changed, the published port for media_port must be updated to match in docker-compose.yaml as well.
is_secure bool No When True, the SDK communicates over https; otherwise http is used. Defaults to False.
backup_folder str No Absolute path on the host machine where .bkp files will be stored and loaded from. If not provided, the SDK's own backups/ folder is used. See Backups.

The constructor does not log into any account on its own - call login afterwards. To discover what accounts already exist in the configured data directory, use the available_accounts property, which is also populated automatically during initialization.

from bot import Account

account = Account()

Use a custom backup folder:

from bot import Account

account = Account(backup_folder=r"C:\\Users\\me\\status-backups")

Connect to a Status Backend running on a different host or port:

from bot import Account

account = Account(
    domain="status-backend.internal",
    port=9090,
    is_secure=True
)

Note: Status Backend must be running before initializing Account. You can launch the backend container with launch_docker_container. If the backend is not reachable on domain:port, calls to login will fail.

Note: When backup_folder is set, backup moves the generated .bkp file out of the SDK's internal backups/ folder into the provided path, and recovery via mnemonic will look in this same folder for .bkp files to auto-load. Make sure the folder exists and is writable.

Methods

login(password, key_uid=None, display_name=None, mnemonic=None, infura_token=None, alchemy_token=None, coingecko_api_key=None)

Login to an existing Status account. If the account does not exist in the initialized data directory, a new account will be created and automatically logged in.

Account creation

After a successful login, the decentralized messenger service is automatically started so the account can send and receive messages.

An account can also be recovered if the mnemonic is passed.

Name Type Required Description
password str Yes Password used to encrypt the account
key_uid str Yes* Unique key identifier of the account. If provided, the account will be logged in directly using this identifier. If not provided, then you must use display_name and password to login.
name str Yes* Display name or ENS name of the account. Used to resolve the key_uid if it is not provided, or to create a new account if one does not already exist. This field is required if an account needs to be recovered with mnemonic.
mnemonic str No The mnemonic from info. Use this field with password and display_name to recover the account. If you have .bkp files, in the backup Docker volume they will be automatically picked up and loaded.

Note: You can pass a different display_name but that will be internal only. When an account is recovered setting display_name can be buggy. Ideally when recovering the account, use the original display_name of the account.
infura_token str No RPC token used by Status Backend for the Ethereum RPC component of the wallet.
alchemy_token str No Used to fetch wallet transactions to fetch wallet transaction history via the Alchemy REST API, so no separate key is needed for transactions.
coingecko_api_key str No API key used by Status Backend to fetch token prices.

Wallet functionality is split into three components, each backed by a token: Ethereum RPC (infura_token), transactions (alchemy_token) and prices (coingecko_api_key). All three must be provided for wallet RPC methods to work - if any is missing, wallet calls raise a WalletNotConfiguredError.

Returns the current Account instance, allowing method chaining.

Login with Display name

from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

The code above is equivalent to the following screen on Status App:

Log in screen

Note: This assumes that display_name and is unique for every key_uid. If there are duplicated display_names then the first found match will be used. You can log in with key_uid if you have display_name duplicates.

Login with ENS

from bot import Account

account = Account()
params = {
    "name": "malte.stateofus.eth",
    "password": "SNTPUMP"
}
account.login(**params)

You can purchase a universal username on Status App:

ENS purchase

Login with key_uid

from bot import Account

account = Account()
params = {
    "key_uid": "0xff2c3...",
    "password": "SNTPUMP"
}
account.login(**params)

Recover account

from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "mnemonic" : "phrase_1 phrase_2 phrase_3 phrase_4 phrase_5 phrase_6 phrase_7 phrase_8 phrase_9 phrase_10 phrase_11 phrase_12"
}
account.login(**params)

The code above is equivalent to the following screen on Status App:

Recover screen

Note: When in recovery mode, the display name is updated on Status App as well so it is consistent locally and to other users.

Wallet setup

from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)

Note: infura_token, alchemy_token and coingecko_api_key can be used when creating, recovering and logging in to an account. All three are required to enable wallet functionality - if any is missing, those calls raise a WalletNotConfiguredError.

logout()

Logout from the currently logged-in Status account. This method also clears the internal account state and stops the active messenger session. This function is also supported in del and when the script automatically finishes.

from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

# Optional - even if not specified __del__ will log you out
account.logout()

Returns the current Account instance. This allows chaining additional operations if needed.

Note: Currently logout works for a single sign in and may break because it does not listen for signals.

backup()

Create a local backup file (.bkp) for the currently loggedin account. The backup is generated by the Status Backend and stored inside the configured Docker backup volume. Each file is uniquely associated with an account. If the backup creation fails, an exception will be raised.

Returns str representing the Docker path of the generated backup file. The returned path refers to the Docker container path where the backup was created. If the backup directory is mounted as a Docker volume, the file will also appear on the host machine in the mapped folder.

The filename is generated by the Status Backend and follows the pattern <suffix>_user_data.bkp, where <suffix> is the last 6 characters of the account's compressed public key. For example, an account whose compressed key ends in abc123 produces abc123_user_data.bkp. Because the suffix is derived deterministically from the account's key, the same account always maps to the same filename, which is how a backup is uniquely associated with its account.

from bot import Account

account = Account(backup_folder=r"C:\\Users\\me\\status-backups")
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

backup_path = account.backup()
print(f"Backup created at: {backup_path}")

Chat

send_message(chat_id, message)

Send a text message to a specific chat. This method currently supports text messages only.

Name Type Required Description
chat_id str Yes Identifier of the chat where the message will be sent. All available chat IDs can be obtained from the chats property.
message str Yes The text message to send.
from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

# This is under the assumption you already have a contact / joined a community
chat = account.chats[0]
account.send_message(chat["id"], "Hello from my Status bot!")

get_messages(chat_id, start_timestamp=None, end_timestamp=None)

Retrieve messages from the specified chat within an optional time range. Messages are returned in descending order (newest to oldest). The method automatically paginates through the backend until all messages in the specified range are collected. This method is ideal for backfilling, batch processing or micro batch processing.

Messages can be fetched from:

  • Direct messages - current contacts and contacts that were later removed
  • Community channels - the bot must have read access from the admin
Name Type Required Description
chat_id str Yes Identifier of the chat. All available chat IDs can be obtained from the chats property.
start_timestamp datetime.datetime No The earliest timestamp to include. Messages older than this value will stop the fetch process.
end_timestamp datetime.datetime No The latest timestamp to include. Messages newer than this value will be skipped.

Returns list[dict] containing message objects. Timestamp fields returned by the backend are automatically converted into datetime.datetime objects.

from bot import Account
import datetime

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

chat = account.chats[0]

messages = account.get_messages(
    chat_id=chat["id"],
    start_timestamp=datetime.datetime(2024, 1, 1)
)

for message in messages:
    print(f"{message['timestamp']}\t{message['text']}")

Note: If there are missing messages in a chat that might be because the node (Status Backend) has not received them yet. They may appear later.

listen_messages()

Listen for new incoming messages in real time. This method yields raw message events as they are received from the Status Backend signal messages.new. This method is ideal for developing real time chat applications

from bot import Account
import datetime
# For terminal readability only
from rich import print as rprint
from rich.pretty import Pretty

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

for msg in account.listen_messages():
    rprint(Pretty(msg))

Note: If you receive multiple messages at once, contacts and chats will grow.

add_contact(public_key, display_name=None)

Send a contact request or approve an existing contact request. The mode depends on how the contact shows up in contacts. Best practice would be to look at the the following contacts keys:

  • has_added_us - bool value to check if the other user has added the account as a friend
  • added - bool value to check if the account has added the other user as a friend
  • mutual - bool value to check if the account and other user are in contacts
  • contact_state - str value to see the account's current state
  • external_contact_state - str value to see the other user's state as it is in your node

Modes:

  • Approve mode - has_added_us is True and added is False
  • Add mode - has_added_us is False
Name Type Required Description
public_key str Yes The contact's Status public key.
display_name str Yes / No Display name for the contact. If the contact already exists in contacts, the display_name parameter is optional and the existing name will be reused. If the contact has never interacted with the bot before, display_name must be provided so the contact can be created locally.

Returns the current Account instance, allowing method chaining.

from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

# Send a contact request
account.add_contact(
    public_key="0x04ebcad...",
    display_name="nickninov"
)

remove_contact(public_key)

Remove a contact or decline a pending contact request. The mode depends on how the contact shows up in contacts. Best practice would be to look at the the following contacts keys:

  • has_added_us - bool value to check if the other user has added the account as a friend
  • added - bool value to check if the account has added the other user as a friend
  • mutual - bool value to check if the account and other user are in contacts
  • contact_state - str value to see the account's current state
  • external_contact_state - str value to see the other user's state as it is in your node

Modes:

  • Remove - has_added_us is True and added is True
  • Reject mode - has_added_us is True
Name Type Required Description
public_key str Yes The contact's Status public key. This value corresponds to the key used in contacts.

Returns bool.

Value Meaning
True The contact was successfully removed or the request was declined.
False The contact does not exist or was already removed.
from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

# NOTE: contacts are returned as a dict for 
# internal class checks and scalability
contact = list(account.contacts.values())[0]

removed = account.remove_contact(contact["public_key"])
print(f"Removed: {removed}")

send_request_community(url)

Send a request to join a community using its invitation URL. The method parses the shared Status community URL and submits a join request using the currently logged-in account. The account's wallet address is provided to the community.

This method works with community invites instead of specific community channel ones. Method is currently unstable.

Name Type Required Description
url str Yes The shared Status community invitation URL.

Returns datetime.datetime representing when the join request was submitted.

from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

account.send_request_community(
    "https://status.app/c/community-invite-link"
)

Wallet

get_tokens()

Retrieve all tokens available in Status Backend across all supported chains.

Returns pd.DataFrame.

Column Type Description
chain_id int Chain ID where the token exists. Matches values from chains.
address str Token contract address.
symbol str Token symbol (e.g. ETH, USDT).
decimals int Number of decimals used for the token.
cross_chain_id str
None
Cross-chain identifier (if available).
source_id str Source list from which the token was fetched.
from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)
available_tokens = account.get_tokens()

get_balance(token_addresses, chain_ids=1, wallets=None, ccy=None)

Retrieve token balances for one or more wallets across specified chains. This method supports querying multiple tokens, chains, and wallets. Balances are adjusted using token decimals. Optionally, values can be converted to fiat currencies.

Returns pd.DataFrame.

Column Type Description
timestamp datetime.datetime Timestamp when the balance was fetched.
wallet_address str Wallet address for which the balance was retrieved.
token_address str Token contract address.
token_symbol str Token symbol (e.g. ETH, USDT).
amount float Token balance (adjusted using token decimals).
chain_id int Chain ID where the token exists.
ccy str Fiat currency (only present if ccy is provided).
price float Token price for 1 token_symbol in the given fiat currency (only present if ccy is provided). If you want to get the amount in the wallet, you must amount * price.
from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)

token_mapping = {
    'ETH': '0x0000000000000000000000000000000000000000',
    'SNT': '0x744d70fdbe2ba4cf95131626614a1763df805b9e',
    'USDT': '0xdac17f958d2ee523a2206206994597c13d831ec7',
    'CELO': '0x9b88d293b7a791e40d36a39765ffd5a1b9b5c349'
}
token_addresses = list(token_mapping.values())
# Returns data for logged in wallet
data = account.get_balance(token_addresses)

Access multuple chains:

from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)

token_mapping = {
    'ETH': '0x0000000000000000000000000000000000000000',
    'SNT': '0x744d70fdbe2ba4cf95131626614a1763df805b9e',
    'USDT': '0xdac17f958d2ee523a2206206994597c13d831ec7',
    'CELO': '0x9b88d293b7a791e40d36a39765ffd5a1b9b5c349'
}
token_addresses = list(token_mapping.values())
chain_ids = [1, 10] # Can be a single int value as well
# Returns data for logged in wallet
data = account.get_balance(token_addresses, chain_ids)

Access multiple wallets:

from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)

token_mapping = {
    'ETH': '0x0000000000000000000000000000000000000000',
    'SNT': '0x744d70fdbe2ba4cf95131626614a1763df805b9e',
    'USDT': '0xdac17f958d2ee523a2206206994597c13d831ec7',
    'CELO': '0x9b88d293b7a791e40d36a39765ffd5a1b9b5c349'
}
token_addresses = list(token_mapping.values())
chain_ids = [1, 10] # Can be a single int value as well

vitalik_address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
bot_wallet = account.info["wallet_address"]
wallets = [bot_wallet, vitalik_address]

data = account.get_balance(token_addresses, chain_ids, wallets)

Get token prices:

from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)

token_mapping = {
    'ETH': '0x0000000000000000000000000000000000000000',
    'SNT': '0x744d70fdbe2ba4cf95131626614a1763df805b9e',
    'USDT': '0xdac17f958d2ee523a2206206994597c13d831ec7',
    'CELO': '0x9b88d293b7a791e40d36a39765ffd5a1b9b5c349'
}
token_addresses = list(token_mapping.values())
chain_ids = [1, 10] # Can be a single int value as well

vitalik_address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
bot_wallet = account.info["wallet_address"]
wallets = [bot_wallet, vitalik_address] # Can be a single str value as well
ccy = ["GBP", "USD"] # Can be a single str value as well

data = account.get_balance(token_addresses, chain_ids, wallets, ccy)

get_market(token_addresses, chain_ids=1, ccy="USD")

Retrieve market data for one or more tokens across specified chains.

Returns pd.DataFrame.

Column Type Description
timestamp datetime.datetime Timestamp when the market data was fetched.
chain_id int Chain ID where the token exists.
token_address str Token contract address.
token_symbol str Token symbol (e.g. ETH, USDT).
fiat_ccy str Fiat currency used for the market data.
market_cap float Total market capitalization of the token.
high_price float Highest price in the last 24 hours.
low_price float Lowest price in the last 24 hours.
pnl_24hr float Absolute price change over the last 24 hours.
pct_change float Percentage price change (day-level).
pct_change_1hr float Percentage price change over the last hour.
pct_change_24hr float Percentage price change over the last 24 hours.

get_transactions(refresh=False)

Retrieve the historical transactions for the logged-in account wallet across all chains in chains. Data is fetched from the Alchemy REST API using the alchemy_token provided during login and combines three transaction types into a single DataFrame: regular transactions (transaction), internal transactions (internal) and ERC-20 token transfers (ERC-20).

Name Type Required Description
refresh bool No When True, the full transaction history is refetched from Alchemy and the cache is replaced. When False (default), the cached DataFrame from the first call is returned.

Returns pd.DataFrame, sorted by timestamp in descending order (newest first).

Column Type Description
timestamp datetime.datetime Block timestamp when the transaction was confirmed.
trx_type str Type of transaction: transaction (regular EOA call), internal (contract-initiated transfer) or ERC-20 (token transfer).
trx_hash str Unique transaction hash. Can be appended to https://etherscan.io/tx/ to inspect the transaction on Etherscan.
from_address str Wallet address that initiated the transaction.
to_address str Wallet address that received the transaction.
movement str sent if the wallet has made the transaction, otherwise received.
token_address str Token contract address.
token_symbol str Token symbol (e.g. ETH, SNT, USDT).
amount float Decimal-adjusted transaction amount. Negative when movement is sent, positive when received.
is_error bool True if the transaction failed on-chain.
chain_id int Chain ID where the transaction occurred. Matches values from chains.
decimals int Number of decimals used to scale amount. Defaults to 18 for native-token transfers.
trx_fee float Gas fee paid in the chain's native token, computed as gas_price * gas_used / 10**18. Populated only for sent rows; 0 for received rows since the receiver does not pay gas.
from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)

transactions = account.get_transactions()
print(transactions.head().to_markdown(index=False))

Force a fresh fetch (e.g. after sending a new transaction with send_transaction):

transactions = account.get_transactions(refresh=True)

send_transaction(address, symbol, amount, chain_id=1)

Send crypto from the logged-in account's wallet to another wallet address on the same chain. This method supports both ETH and ERC-20 tokens. The token can be identified either by its Status symbol (e.g. ETH, SNT, USDT) or by its contract address. Before broadcasting, the method validates that the token exists on the given chain and that the wallet holds enough balance for the requested amount.

Name Type Required Description
address str Yes The wallet address of the receiver.
symbol str Yes Either a valid Status token symbol from get_tokens or the token's contract address (must start with 0x).
amount float Yes The amount of the token to send. Must be less than or equal to the wallet's current balance for that token.
chain_id int No Chain ID where the transaction will be broadcast. Defaults to 1 (Ethereum mainnet). All available chain IDs can be obtained from the chains property.

Returns str representing the transaction hash. The hash can be appended to https://etherscan.io/tx/ to monitor the transaction's progress. The transaction URL is also written to logger at INFO level. If the backend fails to broadcast and does not return a hash, None is returned instead.

Send ETH:

from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)

vitalik_address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"

tx_hash = account.send_transaction(
    address=vitalik_address,
    symbol="ETH",
    amount=0.01
)
print(f"Transaction: https://etherscan.io/tx/{tx_hash}")

Send an ERC-20 token by symbol:

from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)

vitalik_address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"

tx_hash = account.send_transaction(
    address=vitalik_address,
    symbol="SNT",
    amount=10
)

Send an ERC-20 token by contract address:

from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)

vitalik_address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
snt_address = "0x744d70fdbe2ba4cf95131626614a1763df805b9e"

tx_hash = account.send_transaction(
    address=vitalik_address,
    symbol=snt_address,
    amount=10
)

Send on a different chain:

from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)

vitalik_address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"

tx_hash = account.send_transaction(
    address=vitalik_address,
    symbol="ETH",
    amount=0.01,
    chain_id=10 # Optimism
)

Note: This is a wallet method, so it requires infura_token, alchemy_token and coingecko_api_key to all be provided in login. If any is missing, a WalletNotConfiguredError is raised when this method is called.

Note: The sender and receiver must be on the same chain. Cross-chain transfers are not supported by this method - set chain_id to the chain where the funds currently exist.

Note: An exception will be raised when:

  • the symbol (or contract address) does not exist on the given chain_id
  • the token is not present in the logged-in wallet's balance
  • the requested amount exceeds the current wallet balance

swap_tokens(from_token, to_token, amount, chain_id=1)

Swap one token for another on a single chain. The swap happens on a single chain - both from_token and to_token must exist on the given chain_id. Cross-chain swaps are not supported. Swaps are submitted with a fixed slippage tolerance of 0.5%. Each token can be identified either by its Status symbol (e.g. ETH, SNT, USDT) or by its contract address. Before submitting, the method validates that from_token exists in the wallet's balance and that the wallet holds enough of it for the requested amount.

Name Type Required Description
from_token str Yes The token to swap from. Either a valid Status token symbol from get_tokens or the token's contract address (must start with 0x).
to_token str Yes The token to swap to. Either a valid Status token symbol from get_tokens or the token's contract address (must start with 0x).
amount float Yes The amount of from_token to swap. Must be less than or equal to the wallet's current balance for that token.
chain_id int No Chain ID where the swap will happen. Defaults to 1 (Ethereum mainnet). Both from_token and to_token must exist on this chain. All available chain IDs can be obtained from the chains property.

Returns str representing the transaction hash. The hash can be appended to https://etherscan.io/tx/ to monitor the swap's progress. An exception will be raised when:

  • either from_token or to_token is not available on the given chain_id (InvalidTokenError)
  • both from_token and to_token are ERC-20 tokens, i.e. neither side is ETH (InvalidTokenError)
  • from_token is not present in the logged-in wallet's balance, or the requested amount exceeds the current balance (InvalidTokenError)
  • the Status Backend cannot build a swap route or the swap transaction fails (BackendError)

Swap ETH for an ERC-20 token:

from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)

tx_hash = account.swap_tokens(
    from_token="ETH",
    to_token="SNT",
    amount=0.0001
)
print(f"Swap: https://etherscan.io/tx/{tx_hash}")

Swap ERC-20 for an ETH token:

from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)

tx_hash = account.swap_tokens(
    from_token="USDC",
    to_token="ETH",
    amount=100
)
print(f"Swap: https://etherscan.io/tx/{tx_hash}")

Swap ERC-20 for an ERC-20 token:

from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)

tx_hash = account.swap_tokens(
    from_token="USDC",
    to_token="USDT",
    amount=0.01
)
print(f"Swap: https://etherscan.io/tx/{tx_hash}")

Properties

available_accounts

Returns all Status accounts that are locally available in the initialized data directory. These accounts are detected when the Account class is initialized.

This property is useful when you want to:

  • inspect which accounts exist locally
  • retrieve a key_uid for login
  • display metadata about stored accounts

You will have to know the passwords for the given key_uid.

Returns list[dict], one entry per locally available account.

Key Type Description
name str The account's name. For accounts using an ENS name, this is the ENS name (e.g. malte.stateofus.eth). For accounts that do not have an ENS name, this will be their display name.
is_ens bool True when display_name is an ENS name (ends with .eth), otherwise False. Useful for telling apart plain display names from universal usernames.
key_uid str Internal Status key identifier for the account. Can be passed to login as key_uid.
created_at datetime.datetime Timestamp when the account was created locally.
from bot import Account
# For terminal readability only
from rich import print as rprint
from rich.pretty import Pretty

account = Account()

rprint(Pretty(account.available_accounts))

info

Provides information about the currently logged-in account. If login() has not been called, accessing this property will raise an exception. Returns dict containing account metadata.

Key Type Description
public_key str Public key that uniquely identifies the account.
url str The URL that can be shared with other users.
emojis str Emoji hash associated with the account identity.
key_uid str Internal Status key identifier for the account.
compressed_key str The chat key as it is in Status App.
mnemonic str Mnemonic phrase used to generate the account keys.
display_name str Display name of the account.
password str Password used to encrypt the account locally.
wallet_address str Ethereum wallet address associated with the account.
ens dict The account's ENS details. Contains preferred_name (str or None) - the ENS name the account has chosen to display - and usernames (list[dict]) - every ENS username registered to the account. Both are empty / None when no ENS name is set.
logged_in_timestamp datetime.datetime Timestamp when the account successfully logged in.
from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

print(account.info)

display_name

Get or update the current display name of the loggedin account.

Returns str when reading the property.

from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

# Get the current display name
print(account.display_name)

You can update the display name by assigning a new value:

from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

# Change the display name
account.name = "status_bot_42"
print(account.display_name)

Note: Next time you login with the changed display name, you will have to put in the new display name, instead of the initial one.

bio

Get or update the bio of the currently loggedin account. The length of the bio (as in Status App) is 240 characters.

Returns str when reading the property.

from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

# Read the current bio
print(account.bio)

The value assigned to bio will automatically be converted to a string before being sent to the backend. You can update the bio by assigning a new value:

from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

# Update the bio
account.bio = "Monitoring Status communities and chats"
print(account.bio)

You can also clear the bio by deleting the property:

from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

# Clears the bio - same as: 
# account.bio = ""
# account.bio = None
del account.bio

profile_picture

Get or update the profile picture of the currently loggedin account. The image is the same one shown on the user's profile in Status App.

Returns PIL.Image.Image when reading the property, or None if no profile picture has been set.

from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

# Read the current profile picture
image = account.profile_picture
if image:
    image.show()

The file path assigned to profile_picture will be automatically set as the latest profile picture in Status App. If the given file does not exist or the extension is not supported, an exception will be raised. Supported image formats are .jpg, .jpeg and .png.

from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

# Update the profile picture
account.profile_picture = "./full_path/to/my_image.png"
account.profile_picture.show()

When a new profile picture is set, any previous image in the assets folder is removed. The image is also copied into the Status Backend Docker volume so it is picked up by the backend when updating the account identity.

signal

The property exists in Account because signals require an active loggedin session. Attempting to use signals before calling login() will raise an exception. Signals are lowlevel events emitted by the Status Backend.

The property exposes the following methods:

  • signal.get() - fetch a single event. If the event is not found, you may end up in an infinite loop.
  • signal.listen() - stream events continuously. Example usage of this is found in listen_messages()
  • signal.connect() - open a persistent websocket connection that buffers every incoming signal in the background. Must be called before expect().
  • signal.disconnect() - tear down the persistent connection opened by connect().
  • signal.expect() - return a context manager that waits for one or more matching signals to arrive after you perform an action. This is the recommended way to make async message calls, since it removes the race conditions and infinite-loop risk of get().
from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

account.signal.connect()
with account.signal.expect("messages.new") as exp:
    account.send_message(chat_id, "hello")

# Available only after the `with` block exits
print(exp.result)

account.signal.disconnect()

Note: Some signals arrive faster than others - the Status Backend does not emit every signal type at the same speed (for example, a local envelope.sent confirmation typically arrives well before a messages.new event that depends on network propagation). Tune the timeout per signal type, and use count when an action is expected to produce multiple signals, rather than assuming they all land at once or in a fixed order.

logger

Provides access to the internal Python logger for monitoring the lifecycle of the account and backend operations such as login, account creation, messenger startup, and recovery.

Returns logging.Logger.

Default logger configuration:

  • Name: status-bot
  • Level: INFO
  • Output: standard output (terminal)

Example:

from bot import Account

account = Account()

account.logger.info("Starting Status bot")
account.logger.warning("This is a warning")
account.logger.error("Something went wrong")

Chat

contacts

This property returns contacts that have interacted with the account, including:

  • active contacts.
  • users who sent a contact request.
  • users whose contact request was sent by the bot.
  • contacts that were previously removed. If the contact is removed on both sides then it might disappear from the property.

The property always fetches the latest state directly from the Status Backend. The lifecycle is as follows:

  • none - no relationship
  • sent - request sent by this account
  • received - request received from another account
  • mutual - both users have added each other

Returns dict[str, dict] where the key is the contact's public key. This makes internal searching for account specific information faster.

Key Type Description
public_key str Public key that uniquely identifies the contact.
url str The URL that can be shared with other users.
chat_id str Chat identifier used for direct messaging.
compressed_key str Internal compressed key identifier used by Status Backend.
emojis str Emoji hash associated with the contact identity.
contact_state str Current state of the contact relationship (none, mutual, sent, received, dismissed).
external_contact_state str How the contact relationship appears from the other user's perspective.
has_added_us bool Whether the other user has added this account as a contact.
added bool Whether this account has added the other user as a contact.
mutual bool Whether both users have added each other.
display_name str The current display name of the contact.
bio str The contact's profile bio.
last_updated datetime.datetime Timestamp when the contact information was last updated.
from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

contacts = account.contacts

for contact in contacts.values():
    print(contact["display_name"], contact["contact_state"])

communities

Get all communities that the account is currently a member of. This property always fetches the latest community state directly from the Status Backend. This ensures dynamic values such as community metadata, members, and channel permissions are always up to date.

Each community contains information about:

  • community metadata (name, description, tags)
  • membership status
  • number of members
  • available channels and their permissions

Returns list[dict] where each element represents a community.

Key Type Description
id str Unique identifier of the community.
url str The URL that can be shared with other users.
name str Name of the community.
verified bool Whether the community is verified.
description str Community description.
dialog str Intro message shown when joining the community.
leaving_message str Message shown when leaving the community.
tags list[str] Tags associated with the community.
is_member bool Whether the account is currently a member of the community.
joined_timestamp datetime.datetime Timestamp when the account joined the community.
requested_timestamp datetime.datetime Timestamp when the join request was submitted.
encrypted bool Whether the community messaging is encrypted.
members int Total number of community members.
channels list[dict] List of channels available in the community.

Each channel contains:

Key Type Description
id str Channel identifier inside the community.
chat_id str Combined community + channel ID used for sending messages.
url str The URL that can be shared with other users.
name str Channel name.
description str Channel description.
permissions dict Permissions for the channel.

Channel id values can be used directly with send_message

Channel permissions:

Key Type Description
posting bool Whether the account can post messages in the channel.
viewing bool Whether the account can view messages in the channel.
reactions bool Whether the account can react to messages.
token_gated bool Whether the channel requires a token to participate.
from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

for community in account.communities:
    print(community["name"], community["members"])

    for channel in community["channels"]:
        print(f"\t#{channel['name']} posting: {channel['permissions']['posting']}")

community_members

Get member information for all visible communities that the account is in. It can be useful to review community membership, identify suspicious profiles, or filter genuine community members. For each community member, an additional RPC call is made to fetch profile information such as display_name, bio and url. This can make the property slower for larger communities.

Returns pd.DataFrame.

Column Type Description
community_id str Unique identifier of the community.
community_name str Name of the community that the member belongs to.
public_key str Public key that uniquely identifies the community member.
chat_id str Chat identifier used when sending messages.
display_name str Current display name of the member. If unavailable, a fallback name is generated from the compressed key and Status URL.
url str Shareable Status profile URL for the member.
bio str Profile bio of the member, if available.
roles list[int] Roles that the community member has.
compressed_key str The member's compressed chat key as shown in Status App.
emoji_hash str The member's compressed chat key as shown in Status App.
status_alias str Initial display name of the member when the account was created.
from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

members = account.community_members
print(community_members.head().to_markdown(index=False))

chats

Get all chats that the account can send messages to. This includes:

  • contacts - direct messages with users
  • communities - community channels where the account has posting permission
  • Group chats that the account is in

Returns list[dict] where each dict represents a chat that can be used with send_message and get_messages.

Key Type Description
type str Type of chat (contact, channel or group_chat).
id str Chat identifier used when sending messages.
name str Either the display name of the user or the community channel name.
from bot import Account

account = Account()
params = {
    "name": "status-app-bot",
    "password": "SNTPUMP"
}
account.login(**params)

# This is under the assumption you already have a contact / joined a community
for chat in account.chats:
    print(f"{chat['type']}\t{chat['name']}\t{chat['id']}")

Wallet

chains

Retrieve all production blockchain networks available in Status Backend. This property returns a mapping between chain_id and the corresponding chain name.

Returns dict[int, str].

Key Type Description
chain_id int Unique identifier of the blockchain network.
chain_name str Human-readable name of the chain (e.g. Ethereum, Optimism).
from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)
print(account.chains)

balance

Retrieve the current non-zero balances token balances for the logged-in account wallet across all supported chains.

Returns pd.DataFrame.

Column Type Description
timestamp datetime.datetime Timestamp when the balance was fetched.
address str Token contract address.
chain_id int Chain ID where the token exists.
amount float Token balance (adjusted using token decimals).
symbol str Token symbol (e.g. ETH, USDT).
from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)
print(account.balance)

You can convert the current balance into fiat currency by using a ISO 4217 currency code in the [] accessor:

from bot import Account

account = Account()

params = {
    "name": "status-app-bot",
    "password": "SNTPUMP",
    "infura_token": "token from https://www.infura.io/",
    "alchemy_token": "token from https://www.alchemy.com/",
    "coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)
print(account["GBP"])