> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.simjuno.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.simjuno.com/_mcp/server.

# Python SDK

> Install and use the SimJuno Python SDK.

The SimJuno Python SDK provides typed synchronous and asynchronous access to the reseller and eSIM APIs. It includes authentication, retries, timeouts, API errors, and Pydantic request and response models.

The package is configured for publication as `simjuno` on PyPI, and its source repository is available on [GitHub](https://github.com/SimJuno-com/python-sdk).

## Install

```bash
pip install simjuno
```

The SDK requires Python 3.10 or later.

## Create a client

Create an [API key](/authentication), store it in `SIMJUNO_API_KEY`, and initialize the client. Keep the key in server-side code; do not expose it in a browser or mobile application.

```python
import os

from simjuno import SimjunoApi

client = SimjunoApi(api_key=os.environ["SIMJUNO_API_KEY"])

balance = client.reseller.balance()
print(f"Balance: ${balance.balance / 10_000:.2f}")
```

The SDK uses `https://api.simjuno.com/v1` by default and sends the API key in the `x-api-key` header.

## Work with eSIMs

The SDK follows the standard [SimJuno integration flow](/#simjuno-integration-flow). This example finds a plan, confirms its current details, and places an order:

```python
from uuid import uuid4

from simjuno.esim import OrderEsimRequestOrderListItem

package_list = client.esim.list_packages("spain")
if not package_list.packages:
    raise RuntimeError("No packages found")

selected_package = package_list.packages[0]
current_package = client.esim.get_package(selected_package.slug)

order = client.esim.order_esim(
    transaction_id=str(uuid4()),
    order_list=[
        OrderEsimRequestOrderListItem(
            slug=current_package.slug,
            count=1,
        )
    ],
)
```

Ordering eSIMs debits your reseller balance. If you retry the same order, reuse both its `transaction_id` and `order_list`.

After receiving the [`ORDER_STATUS` webhook](/webhook#order-status), retrieve the provisioning details for each returned eSIM ID:

```python
if not order.esim_ids:
    raise RuntimeError("The order returned no eSIM IDs")

esim = client.esim.get_esim(order.esim_ids[0])
print(esim.qr_code_url, esim.short_url, esim.ac)
```

### Available methods

| Task                         | Method                                  |
| ---------------------------- | --------------------------------------- |
| Get reseller balance         | `client.reseller.balance()`             |
| List destinations            | `client.esim.list_destinations()`       |
| List plans for a destination | `client.esim.list_packages(slug)`       |
| Get current plan details     | `client.esim.get_package(slug)`         |
| Order eSIMs                  | `client.esim.order_esim(...)`           |
| List ordered eSIMs           | `client.esim.list_esims()`              |
| Get an eSIM                  | `client.esim.get_esim(id)`              |
| Get current usage            | `client.esim.check_usage(id)`           |
| List top-up plans            | `client.esim.list_topup_packages(slug)` |
| Cancel an eligible eSIM      | `client.esim.cancel_esim(id)`           |

## Handle errors

Non-success responses throw `ApiError`:

```python
from simjuno.core.api_error import ApiError

try:
    client.reseller.balance()
except ApiError as error:
    print(error.status_code, error.body)
    raise
```

## Configure retries and timeouts

Requests time out after 60 seconds and retry up to two times by default for `408`, `409`, `429`, and `5xx` responses. Retries count toward your [rate limit](/rate-limits).

Set defaults when creating the client:

```python
client = SimjunoApi(
    api_key=os.environ["SIMJUNO_API_KEY"],
    max_retries=3,
    timeout=30.0,
)
```

Override them for one request with `request_options`:

```python
destinations = client.esim.list_destinations(
    request_options={"max_retries": 0, "timeout": 10.0}
)
```

## Use the async client

`AsyncSimjunoApi` provides the same methods for asynchronous applications:

```python
import asyncio
import os

from simjuno import AsyncSimjunoApi

async_client = AsyncSimjunoApi(api_key=os.environ["SIMJUNO_API_KEY"])


async def main() -> None:
    balance = await async_client.reseller.balance()
    print(f"Balance: ${balance.balance / 10_000:.2f}")


asyncio.run(main())
```

## Access the raw response

Use `with_raw_response` when you need response headers or status metadata:

```python
response = client.reseller.with_raw_response.balance()
print(response.status_code, response.headers)
balance = response.data
```