Metadata-Version: 2.1
Name: exemplar-python-sdk
Version: 0.1.5
Summary: Python SDK for Exemplar monitoring and heartbeat APIs
Home-page: https://github.com/Exemplar-Dev/exemplar-python-sdk
License: LicenseRef-Proprietary
Keywords: exemplar,monitoring,heartbeat,httpx
Author: Exemplar Team
Requires-Python: >=3.9,<4.0
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Dist: httpx (>=0.25,<1)
Project-URL: Documentation, https://github.com/Exemplar-Dev/exemplar-python-sdk#readme
Project-URL: Repository, https://github.com/Exemplar-Dev/exemplar-python-sdk
Description-Content-Type: text/markdown

# exemplar-python-sdk

Python SDK for [Exemplar](https://exemplar.dev). This package is designed to support multiple Exemplar features over time; currently it includes heartbeat monitor support. The client is a **singleton**: initialize once with your API key, then send heartbeats from sync or async code.

## Install

From [PyPI](https://pypi.org/project/exemplar-python-sdk/) (import name is still `exemplar`):

```bash
pip install exemplar-python-sdk
```

This SDK is distributed for installation via PyPI only.

Requires Python 3.9+.

## Usage

Current supported capability:
- Heartbeat monitors (sync and async)

### 1. Initialize once (API key)

Call `init` at application startup (before any heartbeat). A second `init` raises `RuntimeError`.

```python
import exemplar

exemplar.init(api_key="<personal_access_token>")
# optional: exemplar.init(api_key="...", base_url="https://production-api.exemplar.dev")
```

### 2a. Simple heartbeat (sync)

Use from a cron job, Celery task, Flask/Django view, or any synchronous code:

```python
import exemplar

exemplar.init(api_key=os.environ["EXEMPLAR_API_KEY"])

exemplar.send_heartbeat(
    "69c3a0754e257b7d18094f9a",
    metadata={"hostname": "worker-01"},
)
```

This sends `POST /api/monitoring/heartbeat` with `Content-Type: application/json` and header `X-API-Key: <token>`, matching:

```bash
curl -sS -X POST 'https://production-api.exemplar.dev/api/monitoring/heartbeat' \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: <personal_access_token>' \
  -d '{"monitor_id":"69c3a0754e257b7d18094f9a","metadata":{"hostname":"worker-01"}}'
```

### 2b. Background loop (async)

For FastAPI, Starlette, asyncio services, or any framework that runs an async event loop: start a task that heartbeats on an interval (like a cron in the background):

```python
import asyncio
import exemplar

async def main():
    exemplar.init(api_key="...")
    task = asyncio.create_task(
        exemplar.heartbeat_background_loop(
            "69c3a0754e257b7d18094f9a",
            interval_seconds=60.0,
            metadata={"hostname": "worker-01"},
        )
    )
    try:
        # keep your service alive while heartbeat runs
        await asyncio.sleep(3600)
    finally:
        # graceful shutdown
        task.cancel()
        try:
            await task
        except asyncio.CancelledError:
            pass

if __name__ == "__main__":
    asyncio.run(main())
```

Cancel the task when shutting down. Pass a shared `httpx.AsyncClient` if you want connection reuse with other HTTP calls.

## Development

```bash
poetry install
poetry run pytest
```

## API summary

| Function | Role |
|----------|------|
| `exemplar.init(api_key=..., base_url=...)` | Create singleton (call once) |
| `exemplar.get_client()` | Access the client after `init` |
| `exemplar.send_heartbeat(monitor_id, metadata=...)` | One synchronous POST |
| `exemplar.heartbeat_background_loop(...)` | Async infinite loop with `interval_seconds` |

