> For the complete documentation index, see [llms.txt](https://docs.k9.io/key9-identity/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.k9.io/key9-identity/jsonair/3-using-the-api/3.5-python-examples.md).

# 3.5 Python Examples

The following examples use only the Python standard library. No third-party HTTP client is required.

***

## Authentication — Get a Bearer Token

```python
import json
import urllib.request
import urllib.error


def get_token(base_url: str, pat: str) -> str:
    payload = json.dumps({"token": pat}).encode()
    req = urllib.request.Request(
        f"{base_url}/api/v1/jsonair/auth/token",
        data=payload,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    with urllib.request.urlopen(req) as resp:
        body = json.loads(resp.read())
    return body["access_token"]
```

***

## Fetch Configuration Data

```python
def get_config(
    base_url: str,
    token: str,
    config_type: str,
    config_name: str,
    decode: bool = False,
) -> str:
    payload = json.dumps(
        {"type": config_type, "name": config_name, "decode": decode}
    ).encode()
    req = urllib.request.Request(
        f"{base_url}/api/v1/jsonair/config",
        data=payload,
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json",
        },
        method="GET",
    )
    try:
        with urllib.request.urlopen(req) as resp:
            return resp.read().decode()
    except urllib.error.HTTPError as exc:
        if exc.code == 401:
            raise PermissionError("token expired") from exc
        raise
```

***

## Putting It Together — Poll with Re-Authentication

This pattern mirrors what the JSONAir agent does: authenticate once, poll on an interval, and re-authenticate automatically when the JWT expires.

```python
import base64
import time


BASE_URL = "https://jsonair.example.com"
PAT = "your-plain-text-pat"
CONFIG_TYPE = "myapp"
CONFIG_NAME = "prod.yaml"
INTERVAL = 30  # seconds


def main() -> None:
    token = get_token(BASE_URL, PAT)

    while True:
        try:
            raw = get_config(BASE_URL, token, CONFIG_TYPE, CONFIG_NAME, decode=False)
        except PermissionError:
            print("JWT expired, re-authenticating...")
            token = get_token(BASE_URL, PAT)
            raw = get_config(BASE_URL, token, CONFIG_TYPE, CONFIG_NAME, decode=False)

        config = base64.b64decode(raw).decode()
        print(f"config: {config}")
        time.sleep(INTERVAL)


if __name__ == "__main__":
    main()
```

***

## Fetch the Reload Key

```python
def get_reload(base_url: str, token: str, config_type: str, config_name: str) -> str:
    payload = json.dumps({"type": config_type, "name": config_name}).encode()
    req = urllib.request.Request(
        f"{base_url}/api/v1/jsonair/reload",
        data=payload,
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json",
        },
        method="GET",
    )
    with urllib.request.urlopen(req) as resp:
        body = json.loads(resp.read())
    return body["reload"]
```

***

## Fetch the Debug Level

```python
def get_debug(base_url: str, token: str, config_type: str, config_name: str) -> str:
    payload = json.dumps({"type": config_type, "name": config_name}).encode()
    req = urllib.request.Request(
        f"{base_url}/api/v1/jsonair/debug",
        data=payload,
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json",
        },
        method="GET",
    )
    with urllib.request.urlopen(req) as resp:
        body = json.loads(resp.read())
    return body["debug"]
```
