> ## Documentation Index
> Fetch the complete documentation index at: https://docs.privy.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Learn how to create users, embedded wallets, and send transactions in a Python backend application using the Privy SDK.

## 0. Prerequisites

This guide assumes the [setup](/basics/python/setup) guide is complete and a Privy client instance
named `client` is available.

## 1. Creating a wallet

First, create a wallet. The wallet's `id` is used in future calls to sign messages and send
transactions.

<Tabs>
  <Tab title="Ethereum">
    ```python theme={"system"}
    from privy import APIConnectionError, APIStatusError

    try:
        wallet = client.wallets.create(chain_type="ethereum")
        wallet_id = wallet.id
    except APIStatusError as error:
        # The API returned a non-success status code (4xx or 5xx).
        print(error.status_code, error.message)
        raise
    except APIConnectionError as error:
        # The SDK could not connect to the Privy API.
        print(error.message)
        raise
    ```
  </Tab>

  <Tab title="Solana">
    ```python theme={"system"}
    from privy import APIConnectionError, APIStatusError

    try:
        wallet = client.wallets.create(chain_type="solana")
        wallet_id = wallet.id
    except APIStatusError as error:
        print(error.status_code, error.message)
        raise
    except APIConnectionError as error:
        print(error.message)
        raise
    ```
  </Tab>
</Tabs>

<Tip>[Learn more](/wallets/wallets/create/create-a-wallet) about creating wallets.</Tip>

<Note>
  Errors raised by the SDK inherit from `PrivyAPIError`. Catch `APIStatusError` for non-success API
  responses or a more specific subclass such as `RateLimitError`, `BadRequestError`, or
  `NotFoundError`. Catch `APIConnectionError` for network-level failures.
</Note>

### User wallets

Create a non-custodial user wallet by first creating a user, then provisioning a wallet for that
user.

<Tabs>
  <Tab title="Ethereum">
    ```python theme={"system"}
    user = client.users.create(
        linked_accounts=[
            {"type": "email", "address": "batman@privy.io"},
        ]
    )

    wallet = client.wallets.create(
        chain_type="ethereum",
        owner={"user_id": user.id},
    )
    ```
  </Tab>

  <Tab title="Solana">
    ```python theme={"system"}
    user = client.users.create(
        linked_accounts=[
            {"type": "email", "address": "batman@privy.io"},
        ]
    )

    wallet = client.wallets.create(
        chain_type="solana",
        owner={"user_id": user.id},
    )
    ```
  </Tab>
</Tabs>

<Info>
  When creating a user wallet, specify the user ID as the `owner` of the wallet. Obtain a user ID
  by first [creating a user](/user-management/migrating-users-to-privy/create-or-import-a-user).

  Alternatively, [create a user and wallet at the same
  time](/user-management/migrating-users-to-privy/create-or-import-a-user) by passing `wallets` to
  `client.users.create`.
</Info>

## 2. Signing a message

Next, sign a plaintext message with the wallet using a chain-specific helper. Specify the wallet ID
(not its address) from creation.

<Tabs>
  <Tab title="Ethereum">
    ```python theme={"system"}
    response = client.wallets.ethereum.sign_message(
        wallet_id,
        "Hello, Privy!",
    )

    # The Ethereum signature is hex-encoded.
    signature = response.signature
    ```
  </Tab>

  <Tab title="Solana">
    ```python theme={"system"}
    response = client.wallets.solana.sign_message(
        wallet_id,
        b"Hello, Privy!",
    )

    # The Solana signature is base64-encoded.
    signature = response.signature
    ```
  </Tab>
</Tabs>

<Tip>
  Learn more about signing messages on [Ethereum](/wallets/using-wallets/ethereum/sign-a-message)
  and [Solana](/wallets/using-wallets/solana/sign-a-message).
</Tip>

## 3. Sending transactions

<Info>
  The wallet must have funds to send a transaction. Use a testnet
  [faucet](https://console.optimism.io/faucet) to test transacting on a testnet (for example, Base
  Sepolia), or send funds to the wallet on the network of your choice.
</Info>

Use a chain-specific transaction method to send a transaction. The SDK populates missing
network-related values, signs the transaction, broadcasts it to the network, and returns the
transaction hash.

Specify the wallet `id` from wallet creation and the `caip2` chain ID for the target network.

<Tabs>
  <Tab title="Ethereum">
    ```python theme={"system"}
    response = client.wallets.ethereum.send_transaction(
        wallet_id,
        caip2="eip155:11155111",  # Sepolia testnet
        params={
            "transaction": {
                "to": recipient_address,
                "value": "0x1",  # 1 wei
            }
        },
    )

    transaction_hash = response.hash
    ```
  </Tab>

  <Tab title="Solana">
    ```python theme={"system"}
    # A base64-encoded serialized transaction to sign and send.
    transaction = "insert-base-64-encoded-serialized-transaction"

    response = client.wallets.solana.sign_and_send_transaction(
        wallet_id,
        transaction,
        caip2="solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",  # Solana Devnet
    )

    transaction_hash = response.hash
    ```
  </Tab>
</Tabs>

<Tip>
  Learn more about sending transactions on
  [Ethereum](/wallets/using-wallets/ethereum/send-a-transaction) and
  [Solana](/wallets/using-wallets/solana/send-a-transaction).
</Tip>

<Tip>
  For more control, prepare and broadcast the transaction independently, and use
  `client.wallets.ethereum.sign_transaction` for
  [EVM](/wallets/using-wallets/ethereum/sign-a-transaction) or
  `client.wallets.solana.sign_transaction` for
  [Solana](/wallets/using-wallets/solana/sign-a-transaction).
</Tip>

## 4. Creating a user

To create a user independently of the user-wallet flow above, call `create` on the users service and
pass the linked accounts to associate with the user.

```python theme={"system"}
user = client.users.create(
    linked_accounts=[
        {"type": "custom_auth", "custom_user_id": "your-subject-id"},
        {"type": "email", "address": "user@example.com"},
    ]
)

user_id = user.id
```

<Tip>
  [Learn more](/user-management/migrating-users-to-privy/create-or-import-a-user) about creating
  users, and see the [pregenerating wallets](/recipes/pregenerate-wallets) guide for linking wallets
  to users before they sign in.
</Tip>

## Next steps

<CardGroup cols={2}>
  <Card title="Authorization keys" icon="key" href="/controls/authorization-keys/overview">
    Add an extra layer of security by signing requests with authorization keys.
  </Card>

  <Card title="Policies" icon="shield-check" href="/controls/policies/overview">
    Restrict what wallets can do with configurable policies.
  </Card>

  <Card title="Idempotency keys" icon="fingerprint" href="/api-reference/idempotency-keys">
    Prevent duplicate transactions with idempotency key support.
  </Card>

  <Card title="Quorum approvals" icon="users" href="/controls/quorum-approvals/overview">
    Require multiple parties to approve before sending a transaction.
  </Card>
</CardGroup>
