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

# Python SDK

## Install

```bash
pip install ocpplab
```

## Create a client

```python
import os
from ocpplab import OcpplabSDK

client = OcpplabSDK(token=os.environ["OCPPLAB_TOKEN"])
```

| Argument       | Default                  | Use it to                                                        |
| -------------- | ------------------------ | ---------------------------------------------------------------- |
| `token`        | —                        | Authenticate. See [Authentication](/get-started/authentication). |
| `base_url`     | `https://api.ocpplab.io` | Call another host, such as a local API.                          |
| `timeout`      | `60`                     | Fail a request that takes longer, in seconds.                    |
| `httpx_client` | —                        | Bring your own `httpx.Client`, for example to set a proxy.       |

Per request, pass `request_options={"timeout_in_seconds": 10, "max_retries": 1}`.

## Call the API

Methods follow the API's domains and are snake_case, like the request and response fields.

```python
for brand in client.evcharger.catalog.list_brands():
    print(brand.slug, brand.name)

status = client.evcharger.commands.get_status("cmd-123")
print(status.status)
```

Every method, its parameters and a runnable snippet are in the [API Reference](/api-reference): pick **Python** in any code example.

## Async client

`AsyncOcpplabSDK` has the same methods; await them.

```python
import asyncio
import os
from ocpplab import AsyncOcpplabSDK


async def main() -> None:
    client = AsyncOcpplabSDK(token=os.environ["OCPPLAB_TOKEN"])
    status = await client.evcharger.commands.get_status("cmd-123")
    print(status.status)


asyncio.run(main())
```

## Pagination

List methods return a pager. Iterate it to walk every item, or go page by page:

```python
pager = client.evcharger.catalog.list_brands(limit=20)
for page in pager.iter_pages():
    for brand in page:
        print(brand.slug)
```

## Types

Request and response models are Pydantic models under `ocpplab.types`:

```python
from ocpplab.types import CommandTarget

target = CommandTarget(identity="DEMO-CP-001")
```

## Full example

```python title={"quickstart.py"}
"""Put virtual chargers online against your CSMS, then wait until they connect.

    export OCPPLAB_TOKEN="<api-key>"
    export CSMS_WS_URL="wss://csms.example.com/ocpp"
    uv run python examples/quickstart.py

Creating a location returns 202 at once; the chargers deploy in the background,
so this polls their deployment stage until every one is online.
"""

from __future__ import annotations

import os
import time

from ocpplab import OcpplabSDK
from ocpplab.types import VirtualLocationChargerItem, VirtualLocationCoordinates

IDENTITIES = ["DEMO-CP-001", "DEMO-CP-002"]


def main() -> None:
    # Defaults to https://api.ocpplab.io; pass base_url= for another host.
    client = OcpplabSDK(token=os.environ["OCPPLAB_TOKEN"])
    accepted = client.evcharger.location.create(
        name="Demo hub",
        address="1 Parvis de la Defense",
        city="Puteaux",
        country="FRA",
        coordinates=VirtualLocationCoordinates(lat=48.892, lng=2.238),
        chargers=[
            VirtualLocationChargerItem(
                identity=identity,
                brand_slug="wallbox",
                model="wallbox-pulsar-plus",
                connector_type="Type2",
                ws_url=os.environ["CSMS_WS_URL"],
            )
            for identity in IDENTITIES
        ],
    )
    print(f"location {accepted.location_id}: {accepted.detail}")

    deadline = time.monotonic() + 600
    while True:
        chargers = client.evcharger.deployment_runtime_ops.status(
            identity=IDENTITIES
        ).items
        for charger in chargers:
            print(charger.identity, charger.stage, charger.outcome, charger.hint or "")
        if chargers and all(charger.stage == "online" for charger in chargers):
            return
        if any(charger.outcome in ("failed", "stuck") for charger in chargers):
            raise SystemExit("a charger failed to deploy; see the hint above")
        if time.monotonic() > deadline:
            raise SystemExit("chargers were not online after 10 minutes")
        time.sleep(5)


if __name__ == "__main__":
    main()

```

## Next

* [Errors and retries](/sdks/errors-and-retries)
* [Long-running operations](/get-started/guides/long-running-operations)